Files
kra-oa/internal/data/payment/allinpay.go
T
2026-08-19 18:06:03 +08:00

140 lines
5.0 KiB
Go

package payment
import (
"context"
"errors"
"strings"
"kra/internal/biz"
"github.com/go-pay/gopay"
"github.com/go-pay/gopay/allinpay"
)
type allinpayAdapter struct{}
func (a *allinpayAdapter) client(c map[string]any) (*allinpay.Client, error) {
client, err := allinpay.NewClient(text(c, "cus_id"), text(c, "app_id"), text(c, "private_key"), text(c, "public_key"), !strings.EqualFold(text(c, "environment"), "sandbox"))
if err != nil {
return nil, err
}
if org := text(c, "org_id"); org != "" {
client.SetOrgId(org)
}
return client, nil
}
func (a *allinpayAdapter) Create(ctx context.Context, req *biz.PaymentRequest, c map[string]any) (*biz.PaymentResult, error) {
client, err := a.client(c)
if err != nil {
return nil, err
}
bm := gopay.BodyMap{"reqsn": req.TradeNo, "trxamt": req.Amount, "body": req.Subject}
mergeGoPayExtras(bm, req.Extra, "reqsn", "trxamt", "body", "method", "pay_method", "trade_type")
method := strings.ToLower(firstAny(req.Extra, "method", "pay_method", "trade_type"))
if method == "scan" || method == "micropay" || method == "barcode" {
bm.Set("authcode", firstAny(req.Extra, "authcode", "auth_code"))
bm.SetBodyMap("terminfo", func(info gopay.BodyMap) {
info.Set("devicetype", firstAnyOr(req.Extra, "10", "device_type", "devicetype"))
info.Set("termno", firstAnyOr(req.Extra, "00000001", "termno", "terminal_no"))
})
rsp, callErr := client.ScanPay(ctx, bm)
if callErr != nil {
return nil, callErr
}
if rsp == nil {
return nil, errors.New("通联支付扫码下单响应为空")
}
return &biz.PaymentResult{Provider: biz.PaymentAllinPay, Status: normalizeAllinState(rsp.TrxStatus), TradeNo: req.TradeNo, ProviderTradeNo: rsp.Trxid, Payload: mustJSON(rsp)}, nil
}
if method == "native" || method == "qrcode" {
bm.Set("expiretime", firstAny(req.Extra, "expiretime", "expire_time"))
rsp, callErr := client.NativePay(ctx, bm)
if callErr != nil {
return nil, callErr
}
if rsp == nil {
return nil, errors.New("通联支付下单响应为空")
}
return &biz.PaymentResult{Provider: biz.PaymentAllinPay, Status: "created", TradeNo: req.TradeNo, ProviderTradeNo: rsp.ReqSn, QueryID: rsp.PayInfo, Payload: mustJSON(rsp)}, nil
}
payType := firstAny(req.Extra, "paytype", "pay_type")
if payType == "" {
payType = firstAny(c, "paytype", "pay_type")
}
if payType == "" {
payType = allinpay.PayTypeWXJS
}
bm.Set("paytype", payType)
rsp, err := client.Pay(ctx, bm)
if err != nil {
return nil, err
}
if rsp == nil {
return nil, errors.New("通联支付下单响应为空")
}
return &biz.PaymentResult{Provider: biz.PaymentAllinPay, Status: "created", TradeNo: req.TradeNo, ProviderTradeNo: rsp.Trxid, QueryID: rsp.PayInfo, Payload: mustJSON(rsp)}, nil
}
func (a *allinpayAdapter) Query(ctx context.Context, tradeNo string, c map[string]any) (*biz.PaymentResult, error) {
client, err := a.client(c)
if err != nil {
return nil, err
}
orderType := firstAny(c, "query_order_type", "order_type")
if orderType == "" {
orderType = allinpay.OrderTypeReqSN
}
rsp, err := client.Query(ctx, orderType, tradeNo)
if err != nil {
return nil, err
}
if rsp == nil {
return nil, errors.New("通联支付查单响应为空")
}
result := &biz.PaymentResult{Provider: biz.PaymentAllinPay, Status: normalizeAllinState(rsp.TrxStatus), TradeNo: rsp.Reqsn, ProviderTradeNo: rsp.Trxid, Currency: strings.ToUpper(firstAny(c, "currency")), Payload: mustJSON(rsp)}
if result.TradeNo == "" {
result.TradeNo = tradeNo
}
if rsp.TrxAmt != "" {
result.Amount, err = parseIntegerAmount(rsp.TrxAmt)
if err != nil {
if scale := configuredInt64(c, "amount_scale", 100); scale != 1 {
result.Amount, err = parseDecimalAmount(rsp.TrxAmt, scale)
}
}
}
if result.Currency == "" {
result.Currency = "CNY"
}
return result, err
}
func (a *allinpayAdapter) Refund(ctx context.Context, req *biz.PaymentRefundRequest, c map[string]any) (*biz.PaymentResult, error) {
if req == nil {
return nil, errors.New("通联支付退款请求为空")
}
client, err := a.client(c)
if err != nil {
return nil, err
}
bm := gopay.BodyMap{"reqsn": req.RefundNo, "trxamt": req.Amount, "oldreqsn": req.TradeNo, "remark": firstAny(c, "refund_remark", "remark")}
mergeGoPayConfigExtras(bm, c, "refund_extra", "reqsn", "trxamt", "oldreqsn", "remark")
rsp, err := client.Refund(ctx, bm)
if err != nil {
return nil, err
}
if rsp == nil {
return nil, errors.New("通联支付退款响应为空")
}
refundAmount, _ := parseIntegerAmount(rsp.Fee)
if refundAmount == 0 {
refundAmount = req.Amount
}
return &biz.PaymentResult{Provider: biz.PaymentAllinPay, Status: normalizeRefundState(rsp.TrxStatus), TradeNo: req.TradeNo, ProviderTradeNo: rsp.Trxid, Amount: refundAmount, Currency: strings.ToUpper(firstAny(c, "currency")), Payload: mustJSON(rsp)}, nil
}
func (a *allinpayAdapter) Callback(_ context.Context, _ *biz.PaymentCallback, _ map[string]any) (*biz.PaymentResult, error) {
return nil, errors.New("通联支付回调没有可复用的 GoPay 验签器,请改用主动查单")
}