package pay import ( "context" "yunyan/comm" "yunyan/lego/sys/log" "yunyan/pb" "yunyan/sys/pay/appleiap" "yunyan/sys/pay/googleiap" "yunyan/sys/pay/paypal" "fmt" "strings" "time" ) // @Summary 验证订单 // @Description 验证支付订单是否有效 // @Tags User // @Accept json // @Produce json // @Security BearerAuth // @Param user body pb.PayAskOrCancelReq true "发送绑定请求" // @Success 200 {object} comm.HttpResult{data=pb.PayPayOrderAskResp} "成功返回" // @Router /api/home/pay_verifyorder [post] func (this *apiComp) VerifyOrder(session comm.IUserSession, req *pb.PayAskOrCancelReq) (resp *pb.PayPayOrderAskResp, errdata *pb.ErrorData) { var ( model *pb.DBPayOrder user *pb.DBUser err error ) if req.Orderid == "" { if orders, _, err0 := this.module.model.getUserPayOrders(session.GetUserId(), 1, 50); err0 == nil { for _, o := range orders { if o.Status == pb.PayOrderStatus_PAY_ORDER_PENDING { req.Orderid = o.OrderId break } } } } // 查询订单 if model, err = this.module.model.getPayOrder(req.Orderid); err != nil { errdata = &pb.ErrorData{ Code: pb.ErrorCode_PayOrderNotFound, Message: "查询订单失败:" + fmt.Sprintf("%v", err), } return } // 查询用户 if user, err = this.module.model.getUser(session.GetUserId()); err != nil { errdata = &pb.ErrorData{ Code: pb.ErrorCode_DBError, Message: "查询用户失败:" + fmt.Sprintf("%v", err), } return } this.module.Debug("VerifyOrder", log.Field{Key: "order", Value: model}) // Google/Apple 主动查询与校验:仅在订单未支付时进行 if model.Status == pb.PayOrderStatus_PAY_ORDER_PENDING { ctx, cancel := context.WithTimeout(context.Background(), 8*time.Second) defer cancel() // PayPal 主动执行 capture 扣款 if model.PaymentMethod == pb.PaymentMethod_PayPal { if model.TransactionId == "" { errdata = &pb.ErrorData{Code: pb.ErrorCode_PayOrderNotFound, Message: "缺少 PayPal 订单号,无法执行 capture"} return } captureJSON, err := paypal.CaptureOrder(ctx, model.TransactionId) if err != nil { if this.module.analyze != nil { this.module.analyze.Report(&comm.StatEvent{Type: comm.StatEventOrderFailed, Uid: model.Uid}) } errdata = &pb.ErrorData{Code: pb.ErrorCode_PayCreateOrderFail, Message: "PayPal capture 失败:" + fmt.Sprintf("%v", err)} return } // 简单判定:返回中包含 status=COMPLETED 视为支付成功 if !strings.Contains(captureJSON, "\"status\":\"COMPLETED\"") { if this.module.analyze != nil { this.module.analyze.Report(&comm.StatEvent{Type: comm.StatEventOrderFailed, Uid: model.Uid}) } errdata = &pb.ErrorData{Code: pb.ErrorCode_PayCreateOrderFail, Message: "PayPal capture 未完成"} return } // 扣款成功,更新订单并发放资源 model.Status = pb.PayOrderStatus_PAY_ORDER_PAID model.PayTime = time.Now().Unix() if err = this.deliverResourceByOrder(user, model); err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_ResourceDeliveryError, Message: "发放资源失败:" + fmt.Sprintf("%v", err)} return } if err = this.module.model.updatePayOrder(model); err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_PayOrderStatusError, Message: "更新订单状态失败:" + fmt.Sprintf("%v", err)} return } } // Apple IAP 主动校验 if model.PaymentMethod == pb.PaymentMethod_AppleIAP { if req.AppleReceiptData == "" { errdata = &pb.ErrorData{Code: pb.ErrorCode_ReqParameterError, Message: "缺少苹果收据数据或交易ID"} return } var ok bool if req.AppleUseTransactionId { ok, err = appleiap.VerifyTransaction(ctx, req.AppleReceiptData) } else { ok, err = appleiap.VerifyReceipt(ctx, req.AppleReceiptData) } if err != nil || !ok { if this.module.analyze != nil { this.module.analyze.Report(&comm.StatEvent{Type: comm.StatEventOrderFailed, Uid: model.Uid}) } errdata = &pb.ErrorData{Code: pb.ErrorCode_PayNotifyDecryptFail, Message: "Apple IAP 校验失败:" + fmt.Sprintf("%v", err)} return } // 校验通过,更新订单并发放资源 model.Status = pb.PayOrderStatus_PAY_ORDER_PAID model.PayTime = time.Now().Unix() if err = this.deliverResourceByOrder(user, model); err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_ResourceDeliveryError, Message: "发放资源失败:" + fmt.Sprintf("%v", err)} return } if err = this.module.model.updatePayOrder(model); err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_PayOrderStatusError, Message: "更新订单状态失败:" + fmt.Sprintf("%v", err)} return } } // Google IAP 主动校验 if model.PaymentMethod == pb.PaymentMethod_GoogleIAP { if req.GooglePackageName == "" || req.GoogleProductId == "" || req.GooglePurchaseToken == "" { errdata = &pb.ErrorData{Code: pb.ErrorCode_ReqParameterError, Message: "缺少谷歌校验所需参数"} return } var ok bool if req.GoogleIsSubscription { ok, err = googleiap.VerifySubscriptionPurchase(ctx, req.GooglePackageName, req.GoogleProductId, req.GooglePurchaseToken) } else { ok, err = googleiap.VerifyProductPurchase(ctx, req.GooglePackageName, req.GoogleProductId, req.GooglePurchaseToken) } if err != nil || !ok { if this.module.analyze != nil { this.module.analyze.Report(&comm.StatEvent{Type: comm.StatEventOrderFailed, Uid: model.Uid}) } errdata = &pb.ErrorData{Code: pb.ErrorCode_PayNotifyDecryptFail, Message: "Google IAP 校验失败:" + fmt.Sprintf("%v", err)} return } // 校验通过,更新订单并发放资源 model.Status = pb.PayOrderStatus_PAY_ORDER_PAID model.PayTime = time.Now().Unix() if err = this.deliverResourceByOrder(user, model); err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_ResourceDeliveryError, Message: "发放资源失败:" + fmt.Sprintf("%v", err)} return } if err = this.module.model.updatePayOrder(model); err != nil { errdata = &pb.ErrorData{Code: pb.ErrorCode_PayOrderStatusError, Message: "更新订单状态失败:" + fmt.Sprintf("%v", err)} return } } } else if model.Status == pb.PayOrderStatus_PAY_ORDER_PAID { this.module.Debug("VerifyOrder 已经完成", log.Field{Key: "order", Value: model}) } resp = &pb.PayPayOrderAskResp{ Order: model, User: user, } return }