kra-new/pkg/paymentkit/callback.go

92 lines
2.4 KiB
Go

package paymentutil
import (
"encoding/json"
"errors"
"fmt"
)
const (
ProviderAlipay = "alipay"
ProviderAlipayV3 = "alipay-v3"
ProviderWechatV2 = "wechat-v2"
ProviderWechatV3 = "wechat-v3"
ProviderApple = "apple-iap"
)
// CallbackAck is the provider-facing HTTP acknowledgement returned after a
// payment notification is processed.
type CallbackAck struct {
StatusCode int
ContentType string
Body []byte
}
// CallbackError carries a safe provider acknowledgement alongside the cause
// that should be logged or returned to the application layer.
type CallbackError struct {
Cause error
Ack CallbackAck
}
func (e *CallbackError) Error() string {
if e == nil || e.Cause == nil {
return "支付回调处理失败"
}
return e.Cause.Error()
}
func (e *CallbackError) Unwrap() error {
if e == nil {
return nil
}
return e.Cause
}
func CallbackFailure(err error, fallback CallbackAck) CallbackAck {
var callbackErr *CallbackError
if errors.As(err, &callbackErr) && callbackErr.Ack.StatusCode != 0 {
return callbackErr.Ack
}
return fallback
}
// DefaultCallbackAck returns the conventional acknowledgement for providers
// whose callback wire formats are stable across modules.
func DefaultCallbackAck(provider string, success bool) CallbackAck {
status := 200
if !success {
status = 500
}
switch provider {
case ProviderAlipay, ProviderAlipayV3:
body := "success"
if !success {
body = "failure"
}
return CallbackAck{StatusCode: status, ContentType: "text/plain; charset=utf-8", Body: []byte(body)}
case ProviderWechatV2:
code, message := "SUCCESS", "OK"
if !success {
code, message = "FAIL", "FAIL"
}
body := fmt.Sprintf("<xml><return_code><![CDATA[%s]]></return_code><return_msg><![CDATA[%s]]></return_msg></xml>", code, message)
return CallbackAck{StatusCode: status, ContentType: "application/xml; charset=utf-8", Body: []byte(body)}
case ProviderWechatV3:
code, message := "SUCCESS", "成功"
if !success {
code, message = "FAIL", "失败"
}
body, _ := json.Marshal(map[string]string{"code": code, "message": message})
return CallbackAck{StatusCode: status, ContentType: "application/json; charset=utf-8", Body: body}
case ProviderApple:
return CallbackAck{StatusCode: status}
default:
body := "success"
if !success {
body = "failure"
}
return CallbackAck{StatusCode: status, ContentType: "text/plain; charset=utf-8", Body: []byte(body)}
}
}