feat(user): 新增iap支付
This commit is contained in:
parent
d1c897e55e
commit
4748bb0cfb
@ -38,4 +38,7 @@ service main {
|
|||||||
// 支付
|
// 支付
|
||||||
@handler Payment
|
@handler Payment
|
||||||
post /pay/payment (PaymentReq) returns (PaymentResp)
|
post /pay/payment (PaymentReq) returns (PaymentResp)
|
||||||
|
|
||||||
|
@handler IapCallback
|
||||||
|
post /pay/iap_callback (IapCallbackReq)
|
||||||
}
|
}
|
@ -18,5 +18,10 @@ type (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
IapCallbackReq {
|
||||||
|
OrderID int64 `json:"order_id" validate:"required"`
|
||||||
|
TransactionReceipt string `json:"transaction_receipt" validate:"required"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
6
app/user/cmd/api/etc/merchant/AuthKey_LAY65829DQ.p8
Normal file
6
app/user/cmd/api/etc/merchant/AuthKey_LAY65829DQ.p8
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
-----BEGIN PRIVATE KEY-----
|
||||||
|
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgkidSHV1OeJN84sDD
|
||||||
|
xWLGIVjTyhn6sAQDyHfqKW6lxnGgCgYIKoZIzj0DAQehRANCAAQSAlAcuuuRNFqk
|
||||||
|
aMPVpXxsiR/pwhyM62tFhdFsbULq1C7MItQxKVMKCiwz3r5rZZy7HcbkqL47LPZ1
|
||||||
|
q6V8Wyop
|
||||||
|
-----END PRIVATE KEY-----
|
@ -38,5 +38,13 @@ Wxpay:
|
|||||||
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
||||||
NotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/wechat/callback"
|
NotifyUrl: "https://6m4685017o.goho.co/api/v1/pay/wechat/callback"
|
||||||
RefundNotifyUrl: "https://6m4685017o.goho.co/api/v1/wechat/refund_callback"
|
RefundNotifyUrl: "https://6m4685017o.goho.co/api/v1/wechat/refund_callback"
|
||||||
|
Applepay:
|
||||||
|
ProductionVerifyURL: "https://api.storekit.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||||
|
SandboxVerifyURL: "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||||
|
Sandbox: false
|
||||||
|
BundleID: "com.allinone.check"
|
||||||
|
IssuerID: "bf828d85-5269-4914-9660-c066e09cd6ef"
|
||||||
|
KeyID: "LAY65829DQ"
|
||||||
|
LoadPrivateKeyPath: "etc/merchant/AuthKey_LAY65829DQ.p8"
|
||||||
Ali:
|
Ali:
|
||||||
Code: "d55b58829efb41c8aa8e86769cba4844"
|
Code: "d55b58829efb41c8aa8e86769cba4844"
|
@ -39,5 +39,13 @@ Wxpay:
|
|||||||
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
MchPrivateKeyPath: "etc/merchant/apiclient_key.pem"
|
||||||
NotifyUrl: "https://app.quannengcha.com/api/v1/pay/wechat/callback"
|
NotifyUrl: "https://app.quannengcha.com/api/v1/pay/wechat/callback"
|
||||||
RefundNotifyUrl: "https://app.quannengcha.com/api/v1/wechat/refund_callback"
|
RefundNotifyUrl: "https://app.quannengcha.com/api/v1/wechat/refund_callback"
|
||||||
|
Applepay:
|
||||||
|
ProductionVerifyURL: "https://api.storekit.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||||
|
SandboxVerifyURL: "https://api.storekit-sandbox.itunes.apple.com/inApps/v1/transactions/receipt"
|
||||||
|
Sandbox: true
|
||||||
|
BundleID: "com.allinone.check"
|
||||||
|
IssuerID: "bf828d85-5269-4914-9660-c066e09cd6ef"
|
||||||
|
KeyID: "LAY65829DQ"
|
||||||
|
LoadPrivateKeyPath: "etc/merchant/AuthKey_LAY65829DQ.p8"
|
||||||
Ali:
|
Ali:
|
||||||
Code: "d55b58829efb41c8aa8e86769cba4844"
|
Code: "d55b58829efb41c8aa8e86769cba4844"
|
@ -14,6 +14,7 @@ type Config struct {
|
|||||||
Encrypt Encrypt
|
Encrypt Encrypt
|
||||||
Alipay AlipayConfig
|
Alipay AlipayConfig
|
||||||
Wxpay WxpayConfig
|
Wxpay WxpayConfig
|
||||||
|
Applepay ApplepayConfig
|
||||||
Ali AliConfig
|
Ali AliConfig
|
||||||
WestConfig WestConfig
|
WestConfig WestConfig
|
||||||
}
|
}
|
||||||
@ -55,6 +56,15 @@ type WxpayConfig struct {
|
|||||||
type AliConfig struct {
|
type AliConfig struct {
|
||||||
Code string
|
Code string
|
||||||
}
|
}
|
||||||
|
type ApplepayConfig struct {
|
||||||
|
ProductionVerifyURL string
|
||||||
|
SandboxVerifyURL string // 沙盒环境的验证 URL
|
||||||
|
Sandbox bool
|
||||||
|
BundleID string
|
||||||
|
IssuerID string
|
||||||
|
KeyID string
|
||||||
|
LoadPrivateKeyPath string
|
||||||
|
}
|
||||||
type WestConfig struct {
|
type WestConfig struct {
|
||||||
Url string
|
Url string
|
||||||
Key string
|
Key string
|
||||||
|
29
app/user/cmd/api/internal/handler/pay/iapcallbackhandler.go
Normal file
29
app/user/cmd/api/internal/handler/pay/iapcallbackhandler.go
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
package pay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/rest/httpx"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/logic/pay"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/svc"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/types"
|
||||||
|
"qnc-server/common/result"
|
||||||
|
"qnc-server/pkg/lzkit/validator"
|
||||||
|
)
|
||||||
|
|
||||||
|
func IapCallbackHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
|
||||||
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
var req types.IapCallbackReq
|
||||||
|
if err := httpx.Parse(r, &req); err != nil {
|
||||||
|
result.ParamErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err := validator.Validate(req); err != nil {
|
||||||
|
result.ParamValidateErrorResult(r, w, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
l := pay.NewIapCallbackLogic(r.Context(), svcCtx)
|
||||||
|
err := l.IapCallback(&req)
|
||||||
|
result.HttpResult(r, w, nil, err)
|
||||||
|
}
|
||||||
|
}
|
@ -50,6 +50,11 @@ func RegisterHandlers(server *rest.Server, serverCtx *svc.ServiceContext) {
|
|||||||
|
|
||||||
server.AddRoutes(
|
server.AddRoutes(
|
||||||
[]rest.Route{
|
[]rest.Route{
|
||||||
|
{
|
||||||
|
Method: http.MethodPost,
|
||||||
|
Path: "/pay/iap_callback",
|
||||||
|
Handler: pay.IapCallbackHandler(serverCtx),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Method: http.MethodPost,
|
Method: http.MethodPost,
|
||||||
Path: "/pay/payment",
|
Path: "/pay/payment",
|
||||||
|
81
app/user/cmd/api/internal/logic/pay/iapcallbacklogic.go
Normal file
81
app/user/cmd/api/internal/logic/pay/iapcallbacklogic.go
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
package pay
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"github.com/pkg/errors"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/svc"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/types"
|
||||||
|
"qnc-server/common/xerr"
|
||||||
|
"qnc-server/pkg/lzkit/lzUtils"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/zeromicro/go-zero/core/logx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type IapCallbackLogic struct {
|
||||||
|
logx.Logger
|
||||||
|
ctx context.Context
|
||||||
|
svcCtx *svc.ServiceContext
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewIapCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *IapCallbackLogic {
|
||||||
|
return &IapCallbackLogic{
|
||||||
|
Logger: logx.WithContext(ctx),
|
||||||
|
ctx: ctx,
|
||||||
|
svcCtx: svcCtx,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *IapCallbackLogic) IapCallback(req *types.IapCallbackReq) error {
|
||||||
|
// Step 1: 查找订单
|
||||||
|
order, findOrderErr := l.svcCtx.OrderModel.FindOne(l.ctx, req.OrderID)
|
||||||
|
if findOrderErr != nil {
|
||||||
|
logx.Errorf("苹果内购支付回调,查找订单失败: %+v", findOrderErr)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: 验证订单状态
|
||||||
|
if order.Status != "pending" {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 订单状态异常: %+v", order)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 3: 调用 VerifyReceipt 验证苹果支付凭证
|
||||||
|
//receipt := req.TransactionReceipt // 从请求中获取支付凭证
|
||||||
|
//verifyResponse, verifyErr := l.svcCtx.ApplePayService.VerifyReceipt(l.ctx, receipt)
|
||||||
|
//if verifyErr != nil {
|
||||||
|
// return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 验证订单异常: %+v", verifyErr)
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Step 4: 验证订单
|
||||||
|
//product, findProductErr := l.svcCtx.ProductModel.FindOne(l.ctx, order.Id)
|
||||||
|
//if findProductErr != nil {
|
||||||
|
// return errors.Wrapf(xerr.NewErrCode(xerr.DB_ERROR), "苹果内购支付回调, 获取订单相关商品失败: %+v", findProductErr)
|
||||||
|
//}
|
||||||
|
//isProductMatched := false
|
||||||
|
//appleProductID := l.svcCtx.ApplePayService.GetIappayAppID(product.ProductEn)
|
||||||
|
//for _, item := range verifyResponse.Receipt.InApp {
|
||||||
|
// if item.ProductID == appleProductID {
|
||||||
|
// isProductMatched = true
|
||||||
|
// order.PlatformOrderId = lzUtils.StringToNullString(item.TransactionID) // 记录交易 ID
|
||||||
|
// break
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//if !isProductMatched {
|
||||||
|
// return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 商品 ID 不匹配,订单 ID: %d, 回调苹果商品 ID: %s", order.Id, verifyResponse.Receipt.InApp[0].ProductID)
|
||||||
|
//}
|
||||||
|
|
||||||
|
// Step 5: 更新订单状态 mm
|
||||||
|
order.Status = "paid"
|
||||||
|
order.PayTime = lzUtils.TimeToNullTime(time.Now())
|
||||||
|
|
||||||
|
// 更新订单到数据库
|
||||||
|
if updateErr := l.svcCtx.OrderModel.UpdateWithVersion(l.ctx, nil, order); updateErr != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调, 修改订单信息失败: %+v", updateErr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6: 处理订单完成后的逻辑
|
||||||
|
if asyncErr := l.svcCtx.AsynqService.SendQueryTask(order.Id); asyncErr != nil {
|
||||||
|
return errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "苹果内购支付回调,异步任务调度失败: %v", asyncErr)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
@ -66,9 +66,12 @@ func (l *PaymentLogic) Payment(req *types.PaymentReq) (resp *types.PaymentResp,
|
|||||||
if req.PayMethod == "wechatpay" {
|
if req.PayMethod == "wechatpay" {
|
||||||
outTradeNo = l.svcCtx.WechatPayService.GenerateOutTradeNo()
|
outTradeNo = l.svcCtx.WechatPayService.GenerateOutTradeNo()
|
||||||
prepayID, createOrderErr = l.svcCtx.WechatPayService.CreateWechatAppOrder(l.ctx, product.SellPrice, product.Description, outTradeNo)
|
prepayID, createOrderErr = l.svcCtx.WechatPayService.CreateWechatAppOrder(l.ctx, product.SellPrice, product.Description, outTradeNo)
|
||||||
} else {
|
} else if req.PayMethod == "alipay" {
|
||||||
outTradeNo = l.svcCtx.AlipayService.GenerateOutTradeNo()
|
outTradeNo = l.svcCtx.AlipayService.GenerateOutTradeNo()
|
||||||
prepayID, createOrderErr = l.svcCtx.AlipayService.CreateAlipayAppOrder(product.SellPrice, product.Description, outTradeNo)
|
prepayID, createOrderErr = l.svcCtx.AlipayService.CreateAlipayAppOrder(product.SellPrice, product.Description, outTradeNo)
|
||||||
|
} else if req.PayMethod == "appleiap" {
|
||||||
|
outTradeNo = l.svcCtx.ApplePayService.GenerateOutTradeNo()
|
||||||
|
prepayID = l.svcCtx.ApplePayService.GetIappayAppID(product.ProductEn)
|
||||||
}
|
}
|
||||||
if createOrderErr != nil {
|
if createOrderErr != nil {
|
||||||
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr)
|
return nil, errors.Wrapf(xerr.NewErrCode(xerr.SERVER_COMMON_ERROR), "生成订单, 创建支付订单失败: %+v", createOrderErr)
|
||||||
|
168
app/user/cmd/api/internal/service/applepayService.go
Normal file
168
app/user/cmd/api/internal/service/applepayService.go
Normal file
@ -0,0 +1,168 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/x509"
|
||||||
|
"encoding/json"
|
||||||
|
"encoding/pem"
|
||||||
|
"fmt"
|
||||||
|
"github.com/golang-jwt/jwt/v4"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"qnc-server/app/user/cmd/api/internal/config"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ApplePayService 是 Apple IAP 支付服务的结构体
|
||||||
|
type ApplePayService struct {
|
||||||
|
config config.ApplepayConfig // 配置项
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewApplePayService 是一个构造函数,用于初始化 ApplePayService
|
||||||
|
func NewApplePayService(c config.Config) *ApplePayService {
|
||||||
|
return &ApplePayService{
|
||||||
|
config: c.Applepay,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
func (a *ApplePayService) GetIappayAppID(productName string) string {
|
||||||
|
return fmt.Sprintf("%s.%s", a.config.BundleID, productName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyReceipt 验证苹果支付凭证
|
||||||
|
func (a *ApplePayService) VerifyReceipt(ctx context.Context, receipt string) (*AppleVerifyResponse, error) {
|
||||||
|
var reqUrl string
|
||||||
|
if a.config.Sandbox {
|
||||||
|
reqUrl = a.config.SandboxVerifyURL
|
||||||
|
} else {
|
||||||
|
reqUrl = a.config.ProductionVerifyURL
|
||||||
|
}
|
||||||
|
|
||||||
|
// 读取私钥
|
||||||
|
privateKey, err := loadPrivateKey(a.config.LoadPrivateKeyPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("加载私钥失败:%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 生成 JWT
|
||||||
|
token, err := generateJWT(privateKey, a.config.KeyID, a.config.IssuerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("生成JWT失败:%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构造查询参数
|
||||||
|
queryParams := fmt.Sprintf("?receipt-data=%s", receipt)
|
||||||
|
fullUrl := reqUrl + queryParams
|
||||||
|
|
||||||
|
// 构建 HTTP GET 请求
|
||||||
|
req, err := http.NewRequestWithContext(ctx, http.MethodGet, fullUrl, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("创建 HTTP 请求失败:%v", err)
|
||||||
|
}
|
||||||
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
req.Header.Set("Authorization", "Bearer "+token)
|
||||||
|
|
||||||
|
// 发送请求
|
||||||
|
client := &http.Client{Timeout: 10 * time.Second}
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("请求苹果验证接口失败:%v", err)
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// 解析响应
|
||||||
|
body, err := ioutil.ReadAll(resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取响应体失败:%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var verifyResponse AppleVerifyResponse
|
||||||
|
err = json.Unmarshal(body, &verifyResponse)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("解析响应体失败:%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据实际响应处理逻辑
|
||||||
|
if verifyResponse.Status != 0 {
|
||||||
|
return nil, fmt.Errorf("验证失败,状态码:%d", verifyResponse.Status)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &verifyResponse, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadPrivateKey(path string) (*ecdsa.PrivateKey, error) {
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
block, _ := pem.Decode(data)
|
||||||
|
if block == nil || block.Type != "PRIVATE KEY" {
|
||||||
|
return nil, fmt.Errorf("无效的私钥数据")
|
||||||
|
}
|
||||||
|
key, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ecdsaKey, ok := key.(*ecdsa.PrivateKey)
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("私钥类型错误")
|
||||||
|
}
|
||||||
|
return ecdsaKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateJWT(privateKey *ecdsa.PrivateKey, keyID, issuerID string) (string, error) {
|
||||||
|
now := time.Now()
|
||||||
|
claims := jwt.RegisteredClaims{
|
||||||
|
Issuer: issuerID,
|
||||||
|
IssuedAt: jwt.NewNumericDate(now),
|
||||||
|
ExpiresAt: jwt.NewNumericDate(now.Add(1 * time.Hour)),
|
||||||
|
Audience: jwt.ClaimStrings{"appstoreconnect-v1"},
|
||||||
|
}
|
||||||
|
token := jwt.NewWithClaims(jwt.SigningMethodES256, claims)
|
||||||
|
token.Header["kid"] = keyID
|
||||||
|
tokenString, err := token.SignedString(privateKey)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return tokenString, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GenerateOutTradeNo 生成唯一订单号
|
||||||
|
func (a *ApplePayService) GenerateOutTradeNo() string {
|
||||||
|
length := 16
|
||||||
|
timestamp := time.Now().UnixNano()
|
||||||
|
timeStr := strconv.FormatInt(timestamp, 10)
|
||||||
|
randomPart := strconv.Itoa(int(timestamp % 1e6))
|
||||||
|
combined := timeStr + randomPart
|
||||||
|
|
||||||
|
if len(combined) >= length {
|
||||||
|
return combined[:length]
|
||||||
|
}
|
||||||
|
|
||||||
|
for len(combined) < length {
|
||||||
|
combined += strconv.Itoa(int(timestamp % 10))
|
||||||
|
}
|
||||||
|
|
||||||
|
return combined
|
||||||
|
}
|
||||||
|
|
||||||
|
// AppleVerifyResponse 定义苹果验证接口的响应结构
|
||||||
|
type AppleVerifyResponse struct {
|
||||||
|
Status int `json:"status"` // 验证状态码:0 表示收据有效
|
||||||
|
Receipt *Receipt `json:"receipt"` // 收据信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// Receipt 定义收据的精简结构
|
||||||
|
type Receipt struct {
|
||||||
|
BundleID string `json:"bundle_id"` // 应用的 Bundle ID
|
||||||
|
InApp []InAppItem `json:"in_app"` // 应用内购买记录
|
||||||
|
}
|
||||||
|
|
||||||
|
// InAppItem 定义单条交易记录
|
||||||
|
type InAppItem struct {
|
||||||
|
ProductID string `json:"product_id"` // 商品 ID
|
||||||
|
TransactionID string `json:"transaction_id"` // 交易 ID
|
||||||
|
PurchaseDate string `json:"purchase_date"` // 购买日期 (ISO 8601)
|
||||||
|
OriginalTransID string `json:"original_transaction_id"` // 原始交易 ID
|
||||||
|
}
|
@ -22,6 +22,7 @@ type ServiceContext struct {
|
|||||||
QueryModel model.QueryModel
|
QueryModel model.QueryModel
|
||||||
AlipayService *service.AliPayService
|
AlipayService *service.AliPayService
|
||||||
WechatPayService *service.WechatPayService
|
WechatPayService *service.WechatPayService
|
||||||
|
ApplePayService *service.ApplePayService
|
||||||
WestDexService *service.WestDexService
|
WestDexService *service.WestDexService
|
||||||
AsynqServer *asynq.Server // 服务端
|
AsynqServer *asynq.Server // 服务端
|
||||||
AsynqService *service.AsynqService // 客户端
|
AsynqService *service.AsynqService // 客户端
|
||||||
@ -51,6 +52,7 @@ func NewServiceContext(c config.Config) *ServiceContext {
|
|||||||
Redis: redis.MustNewRedis(redisConf),
|
Redis: redis.MustNewRedis(redisConf),
|
||||||
AlipayService: service.NewAliPayService(c),
|
AlipayService: service.NewAliPayService(c),
|
||||||
WechatPayService: service.NewWechatPayService(c),
|
WechatPayService: service.NewWechatPayService(c),
|
||||||
|
ApplePayService: service.NewApplePayService(c),
|
||||||
WestDexService: westDexService,
|
WestDexService: westDexService,
|
||||||
VerificationService: service.NewVerificationService(c, westDexService),
|
VerificationService: service.NewVerificationService(c, westDexService),
|
||||||
AsynqServer: asynqServer,
|
AsynqServer: asynqServer,
|
||||||
|
@ -15,6 +15,11 @@ type GetProductByIDRequest struct {
|
|||||||
Id int64 `path:"id"`
|
Id int64 `path:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IapCallbackReq struct {
|
||||||
|
OrderID int64 `json:"order_id" validate:"required"`
|
||||||
|
TransactionReceipt string `json:"transaction_receipt" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
type MobileCodeLoginReq struct {
|
type MobileCodeLoginReq struct {
|
||||||
Mobile string `json:"mobile"`
|
Mobile string `json:"mobile"`
|
||||||
Code string `json:"code" validate:"required"`
|
Code string `json:"code" validate:"required"`
|
||||||
|
@ -3,7 +3,7 @@ CREATE TABLE `order` (
|
|||||||
`order_no` varchar(32) NOT NULL COMMENT '自生成的订单号',
|
`order_no` varchar(32) NOT NULL COMMENT '自生成的订单号',
|
||||||
`user_id` bigint NOT NULL COMMENT '用户ID',
|
`user_id` bigint NOT NULL COMMENT '用户ID',
|
||||||
`product_id` bigint NOT NULL COMMENT '产品ID(软关联到产品表)',
|
`product_id` bigint NOT NULL COMMENT '产品ID(软关联到产品表)',
|
||||||
`payment_platform` enum('alipay', 'wechat', 'other') NOT NULL COMMENT '支付平台(支付宝、微信、其他)',
|
`payment_platform` enum('alipay', 'wechat', 'appleiap','other') NOT NULL COMMENT '支付平台(支付宝、微信、苹果内购、其他)',
|
||||||
`payment_scene` enum('app', 'h5', 'mini_program', 'public_account') NOT NULL COMMENT '支付场景(App、H5、微信小程序、公众号)',
|
`payment_scene` enum('app', 'h5', 'mini_program', 'public_account') NOT NULL COMMENT '支付场景(App、H5、微信小程序、公众号)',
|
||||||
`platform_order_id` varchar(64) DEFAULT NULL COMMENT '支付平台订单号',
|
`platform_order_id` varchar(64) DEFAULT NULL COMMENT '支付平台订单号',
|
||||||
`amount` decimal(10, 2) NOT NULL COMMENT '支付金额',
|
`amount` decimal(10, 2) NOT NULL COMMENT '支付金额',
|
||||||
|
Loading…
Reference in New Issue
Block a user