92 lines
2.4 KiB
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)}
|
|
}
|
|
}
|