157 lines
5.7 KiB
Go
157 lines
5.7 KiB
Go
package pay
|
||
|
||
import (
|
||
"context"
|
||
"github.com/go-pay/gopay/apple"
|
||
"github.com/gogf/gf/v2/errors/gerror"
|
||
"strings"
|
||
"sync"
|
||
"time"
|
||
)
|
||
|
||
// ApplePay 苹果支付
|
||
// 这是一个用于处理苹果支付的结构体。
|
||
type ApplePay struct {
|
||
pass string // pass 是用于苹果支付过程中的密钥。
|
||
lock sync.RWMutex // lock 用于确保在并发访问或修改 pass 时的安全性。
|
||
}
|
||
|
||
// Init 是ApplePay类型的初始化函数。
|
||
//
|
||
// @Description: 对ApplePay对象进行初始化,将传入的数据存储到对象中。
|
||
// @receiver p: ApplePay对象的指针,用于接收初始化操作。
|
||
// @param data: 一个字节切片,包含需要初始化的数据。
|
||
func (p *ApplePay) Init(data []byte) {
|
||
p.lock.Lock() // 加锁以保证在多线程环境下的线程安全
|
||
defer p.lock.Unlock() // 确保在函数执行完毕退出时自动解锁,避免死锁
|
||
p.pass = string(data) // 将传入的字节切片数据转换为字符串,并赋值给pass字段
|
||
}
|
||
|
||
// VerifyPay 验证苹果支付
|
||
//
|
||
// @Description: 验证苹果支付的收据信息,以确认支付的有效性。
|
||
// @receiver p *ApplePay: ApplePay对象,用于执行验证支付的操作。
|
||
// @param userId uint64: 用户ID。
|
||
// @param OrderId string: 订单ID。
|
||
// @param package1 string: 付费产品的包装名称。
|
||
// @param subscriptionID string: 订阅ID。
|
||
// @param purchaseToken string: 购买令牌,用于苹果服务器的收据验证。
|
||
// @param isDebug bool: 是否为调试模式,决定使用哪个验证URL。
|
||
// @param cb func(string) error: 回调函数,用于处理验证成功后的产品ID。
|
||
// @return error: 返回错误信息,如果验证过程中出现错误,则返回相应的错误信息。
|
||
func (p *ApplePay) VerifyPay(userId uint64, OrderId, package1, subscriptionID, purchaseToken string, isDebug bool, cb func(string) error) error {
|
||
p.lock.RLock() // 加读锁,保证并发安全
|
||
defer p.lock.RUnlock() // 解读锁,确保函数执行完毕后释放锁
|
||
// 根据是否为调试模式选择验证URL
|
||
url := apple.UrlProd
|
||
if isDebug {
|
||
url = apple.UrlSandbox
|
||
}
|
||
// 向苹果服务器验证收据
|
||
info, err := apple.VerifyReceipt(context.Background(), url, p.pass, purchaseToken)
|
||
if err != nil {
|
||
// 如果验证失败,则返回错误
|
||
return err
|
||
}
|
||
// 检查收据验证的状态
|
||
if info.Status == 0 {
|
||
// 检查收据中是否包含内购信息
|
||
if len(info.Receipt.InApp) <= 0 {
|
||
return gerror.Wrap(err, "info.Receipt.InApp = 0")
|
||
}
|
||
// 调用回调函数处理商品ID
|
||
if err := cb(info.Receipt.InApp[0].ProductId); err != nil {
|
||
// 如果回调处理失败,则返回错误
|
||
return err
|
||
}
|
||
} else {
|
||
// 如果收据验证状态异常,则返回状态错误信息
|
||
return gerror.Wrapf(err, "status err = %v", info.Status)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// VerifyPayV1 验证苹果支付的交易
|
||
//
|
||
// @Description:
|
||
// @receiver p
|
||
// @param purchaseToken
|
||
// @param isDebug
|
||
// @param cb
|
||
// @return error
|
||
func (p *ApplePay) VerifyPayV1(purchaseToken string, isDebug bool, cb func(string, string) error) error {
|
||
p.lock.RLock() // 加读锁,确保并发安全
|
||
defer p.lock.RUnlock() // 结束时自动释放读锁
|
||
// 根据调试模式选择验证服务的URL
|
||
url := apple.UrlProd
|
||
if isDebug {
|
||
url = apple.UrlSandbox
|
||
}
|
||
// 向苹果服务器验证收据
|
||
info, err := apple.VerifyReceipt(context.Background(), url, p.pass, purchaseToken)
|
||
if err != nil {
|
||
// 验证失败,返回错误
|
||
return err
|
||
}
|
||
// 检查验证结果状态
|
||
if info.Status == 0 {
|
||
// 验证成功,检查收据中是否有内购信息
|
||
if len(info.Receipt.InApp) <= 0 {
|
||
// 收据中无内购信息,返回错误
|
||
return gerror.Wrap(err, "info.Receipt.InApp = 0")
|
||
}
|
||
// 调用回调函数,处理内购产品信息
|
||
if err := cb(info.Receipt.InApp[0].ProductId, info.Receipt.InApp[0].OriginalTransactionId); err != nil {
|
||
// 回调函数执行失败,返回错误
|
||
return gerror.Wrap(err, "回调函数执行失败")
|
||
}
|
||
} else {
|
||
// 验证结果状态异常,返回错误
|
||
return gerror.Wrapf(err, "status err = %v", info.Status)
|
||
}
|
||
// 验证成功,返回nil
|
||
return nil
|
||
}
|
||
|
||
// VerifyPayTest 用于验证苹果支付的测试购买。
|
||
//
|
||
// @Description:
|
||
// @receiver p
|
||
// @param purchaseToken
|
||
// @return interface{}
|
||
// @return error
|
||
func (p *ApplePay) VerifyPayTest(purchaseToken string) (interface{}, error) {
|
||
// 使用沙箱环境的URL进行验证
|
||
url := apple.UrlSandbox
|
||
// 调用apple.VerifyReceipt进行收据验证
|
||
return apple.VerifyReceipt(context.Background(), url, p.pass, purchaseToken)
|
||
}
|
||
|
||
// GetTime 根据提供的 timer 字符串解析时间,格式为 "YYYY-MM-DD HH:MM:SS ZZZ",若解析失败则返回当前时间
|
||
//
|
||
// @Description: 根据指定格式解析时间字符串,如果解析失败或者格式不正确,则返回当前时间。
|
||
// @param timer 时间字符串,格式为 "YYYY-MM-DD HH:MM:SS ZZZ",其中 ZZZ 为时区标识。
|
||
// @return time.Time 解析得到的时间,若失败则返回当前时间。
|
||
func GetTime(timer string) time.Time {
|
||
// 将 timer 字符串按空格分割为年月日和时分秒两部分
|
||
ts := strings.Split(timer, "")
|
||
// 如果分割后的数组长度不为3,则说明格式不正确,返回当前时间
|
||
if len(ts) != 3 {
|
||
return time.Now()
|
||
}
|
||
// 尝试加载指定时区信息
|
||
location, err := time.LoadLocation(ts[2])
|
||
// 如果加载时区失败,则返回当前时间
|
||
if err != nil {
|
||
return time.Now()
|
||
}
|
||
// 使用指定时区解析时间字符串
|
||
t, err := time.ParseInLocation("2006-01-02 15:04:05 MST", ts[0]+" "+ts[1], location)
|
||
// 如果解析失败,则返回当前时间
|
||
if err != nil {
|
||
return time.Now()
|
||
}
|
||
// 将解析得到的时间转换为本地时间并返回
|
||
return t.In(time.Local)
|
||
}
|