139 lines
5.1 KiB
Go
139 lines
5.1 KiB
Go
package payment
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
|
|
"kra/internal/biz"
|
|
|
|
"github.com/go-pay/gopay"
|
|
"github.com/go-pay/gopay/lakala"
|
|
)
|
|
|
|
type lakalaAdapter struct{}
|
|
|
|
func (a *lakalaAdapter) client(c map[string]any) (*lakala.Client, error) {
|
|
return lakala.NewClient(text(c, "partner_code"), text(c, "credential_code"), !strings.EqualFold(text(c, "environment"), "sandbox"))
|
|
}
|
|
|
|
func (a *lakalaAdapter) 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{"description": req.Subject, "price": req.Amount, "channel": firstAny(req.Extra, "channel", "pay_channel")}
|
|
if bm.GetString("channel") == gopay.NULL {
|
|
bm.Set("channel", firstAnyOr(c, "Wechat", "channel", "pay_channel"))
|
|
}
|
|
mergeGoPayExtras(bm, req.Extra, "description", "price", "channel", "pay_channel", "method", "pay_method", "trade_type")
|
|
method := strings.ToLower(firstAny(req.Extra, "method", "pay_method", "trade_type"))
|
|
if method == "h5" {
|
|
rsp, callErr := client.CreateH5PayOrder(ctx, req.TradeNo, bm)
|
|
if callErr != nil {
|
|
return nil, callErr
|
|
}
|
|
return lakalaCreateResult(req.TradeNo, rsp)
|
|
}
|
|
rsp, err := client.CreateJSAPIOrder(ctx, req.TradeNo, bm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return lakalaCreateResult(req.TradeNo, rsp)
|
|
}
|
|
|
|
func lakalaCreateResult(tradeNo string, rsp *lakala.PaymentRsp) (*biz.PaymentResult, error) {
|
|
if rsp == nil || !strings.EqualFold(rsp.ReturnCode, "SUCCESS") && !strings.EqualFold(rsp.ResultCode, "SUCCESS") {
|
|
return nil, fmt.Errorf("拉卡拉支付下单失败: %v", rsp)
|
|
}
|
|
return &biz.PaymentResult{Provider: biz.PaymentLakala, Status: "created", TradeNo: tradeNo, ProviderTradeNo: rsp.OrderId, QueryID: first(rspMap(rsp), "code_url", "pay_url", "sdk_params"), Payload: mustJSON(rsp)}, nil
|
|
}
|
|
|
|
func (a *lakalaAdapter) Query(ctx context.Context, tradeNo string, c map[string]any) (*biz.PaymentResult, error) {
|
|
client, err := a.client(c)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
orderID := firstAny(c, "order_id", "provider_order_id")
|
|
if orderID == "" {
|
|
orderID = tradeNo
|
|
}
|
|
rsp, err := client.OrderStatus(ctx, orderID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if rsp == nil || !strings.EqualFold(rsp.ReturnCode, "SUCCESS") && !strings.EqualFold(rsp.ResultCode, "SUCCESS") {
|
|
return nil, fmt.Errorf("拉卡拉支付查单失败: %v", rsp)
|
|
}
|
|
status := normalizeLakalaState(first(rspMap(rsp), "status", "channel_error_code"))
|
|
if rsp.ChannelOrderId != "" && status == "pending" {
|
|
status = "success"
|
|
}
|
|
returnedTradeNo := rsp.PartnerOrderId
|
|
if returnedTradeNo == "" {
|
|
returnedTradeNo = tradeNo
|
|
}
|
|
result := &biz.PaymentResult{Provider: biz.PaymentLakala, Status: status, TradeNo: returnedTradeNo, ProviderTradeNo: rsp.ChannelOrderId, Amount: int64(rsp.TotalFee), Currency: strings.ToUpper(rsp.Currency), Payload: mustJSON(rsp)}
|
|
if result.Currency == "" {
|
|
result.Currency = strings.ToUpper(firstAny(c, "currency", "default_currency"))
|
|
}
|
|
if result.Currency == "" {
|
|
result.Currency = "JPY"
|
|
}
|
|
if rsp.RealFee > 0 {
|
|
result.PayerPaidAmount = int64(rsp.RealFee)
|
|
result.CashPaidAmount = result.PayerPaidAmount
|
|
result.DiscountAmount = result.Amount - result.PayerPaidAmount
|
|
if result.DiscountAmount >= 0 {
|
|
result.AmountBreakdownKnown = true
|
|
}
|
|
}
|
|
return result, nil
|
|
}
|
|
|
|
func (a *lakalaAdapter) 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
|
|
}
|
|
orderID := firstAny(c, "order_id", "provider_order_id")
|
|
if orderID == "" {
|
|
orderID = req.TradeNo
|
|
}
|
|
bm := gopay.BodyMap{"fee": req.Amount}
|
|
if req.Currency != "" {
|
|
bm.Set("currency", strings.ToUpper(req.Currency))
|
|
}
|
|
mergeGoPayConfigExtras(bm, c, "refund_extra", "fee", "currency")
|
|
rsp, err := client.ApplyRefund(ctx, orderID, req.RefundNo, bm)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if rsp == nil || !strings.EqualFold(rsp.ReturnCode, "SUCCESS") && !strings.EqualFold(rsp.ResultCode, "SUCCESS") {
|
|
return nil, fmt.Errorf("拉卡拉支付退款失败: %v", rsp)
|
|
}
|
|
return &biz.PaymentResult{Provider: biz.PaymentLakala, Status: "pending", TradeNo: req.TradeNo, ProviderTradeNo: rsp.RefundId, Amount: int64(rsp.Amount), Currency: strings.ToUpper(rsp.Currency), Payload: mustJSON(rsp)}, nil
|
|
}
|
|
|
|
func (a *lakalaAdapter) Callback(_ context.Context, callback *biz.PaymentCallback, c map[string]any) (*biz.PaymentResult, error) {
|
|
if callback == nil {
|
|
return nil, errors.New("拉卡拉支付回调为空")
|
|
}
|
|
request, err := callbackRequest(callback)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
notify, err := lakala.ParseNotify(request)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if err := lakala.VerifySign(notify, text(c, "partner_code"), text(c, "credential_code")); err != nil {
|
|
return nil, err
|
|
}
|
|
return &biz.PaymentResult{Provider: biz.PaymentLakala, Status: "success", TradeNo: notify.PartnerOrderId, ProviderTradeNo: notify.ChannelOrderId, Amount: int64(notify.TotalFee), Currency: strings.ToUpper(notify.Currency), Payload: mustJSON(notify)}, nil
|
|
}
|