Documentation
¶
Overview ¶
Package payment 提供统一支付领域接口、校验、状态与回调模型。
具体实现位于 adapter 子包:
- payment/adapter/alipay:支付宝当面付、WAP、查询、退款、关单、支付回调;支持密钥/证书加签与正式/沙箱环境(详见该包 doc.go)
- payment/adapter/wechat:微信 Native、JSAPI、OAuth、查询、退款、关单、支付与退款回调;支持微信支付公钥/平台证书验签(详见该包 doc.go)
- payment/adapter/onepay:同一中立二维码按扫码客户端路由微信或支付宝
- payment/adapter/mock:并发安全、状态可控的内存支付测试实现;支持本地无密钥完整链路(PayAndDeliver 等)
金额一律使用分为单位的 int64,不使用浮点数。所有网络操作接收 context.Context。调用方应给 context 设置 deadline,并对不确定的网关写请求先 查询、后决定是否重试。
本地测试(无真实商户配置) ¶
日常本地与 CI 不要依赖微信/支付宝密钥。业务代码依赖 payment.Payment / PaymentNotifier 等接口,测试与本地环境注入 payment/adapter/mock:
client, _ := mock.New() result, notify, resp, _ := client.PayAndDeliver(ctx, order) // 用 notify 幂等入账,成功后写出 resp
环境变量建议 PAYMENT_PROVIDER=mock|wechat|alipay;用 payment.ParseProvider 解析渠道后 switch 装配。mock 路径:mock.NewForProvider;真实渠道在业务侧 wechat.New / alipay.New。adapter 协议级单测用自签密钥,不要求出网。 详见 payment/adapter/mock 包文档。
基础支付 ¶
result, err := provider.Pay(ctx, &payment.Order{
OrderID: "order-1001",
Amount: 9900,
Subject: "会员订阅",
NotifyURL: "https://merchant.example/payment/notify",
})
PaymentResult.PayURL 是支付宝 qr_code 或微信 code_url;二维码图片由调用方渲染。
支付宝适配器构造示例见 payment/adapter/alipay 包文档:密钥或证书加签、 EnvSandbox / EnvProduction 环境切换。
回调 ¶
adapter 先验签并解析通知。调用方仍须在数据库事务内核对订单、金额、状态并 幂等入账;只有业务事务成功后,才能写入 SuccessResponse。解析成功本身不代表 业务处理成功。
一码付 ¶
onepay 创建的是中立 HTTPS URL 与 PNG。微信扫码后执行 OAuth snsapi_base 与 JSAPI 支付;支付宝扫码后进入 WAP 收银台。业务必须实现 CheckoutResolver, 持久化并复用完整 WAP 或 JSAPI artifact。同一 OpenID 重复微信扫码复用未过期 JSAPI 参数;不同 provider 使用不同订单号。首个成功回调应原子完成主支付意图, 随后关闭另一平台仍待支付的订单。
内存骨架与挂载示例见 payment/adapter/onepay 的 ExampleCheckoutResolver、 ExampleNew_createCodeAndMount(骨架勿直接用于生产)。通用回调 handler 形态见本包 Example_paymentNotifyHandler(仅 success 入账;忽略状态也写 SuccessResponse)。
Example (PaymentNotifyHandler) ¶
Example_paymentNotifyHandler 演示依赖 PaymentNotifier 的通用回调 handler。 生产注入 alipay/wechat adapter;本地/CI 注入 mock。
package main
import (
"context"
"fmt"
"log"
"net/http"
"net/http/httptest"
"github.com/f2xme/gox/payment"
paymock "github.com/f2xme/gox/payment/adapter/mock"
)
func main() {
client, err := paymock.New()
if err != nil {
log.Fatal(err)
}
// 先完成一笔本地支付,得到可解析的回调请求。
_, notify, _, err := client.PayAndDeliver(context.Background(), &payment.Order{
OrderID: "order-1001",
Amount: 9900,
Subject: "会员订阅",
NotifyURL: "https://merchant.example/payment/notify",
})
if err != nil {
log.Fatal(err)
}
// 业务 HTTP 层:验签解析 → 事务入账 → 成功后 ACK。
handler := paymentNotifyHandler(client)
rec := httptest.NewRecorder()
// 重新生成一份回调请求供 handler 消费(Body 只能读一次)。
req, err := client.PaymentNotificationRequest("order-1001")
if err != nil {
log.Fatal(err)
}
handler.ServeHTTP(rec, req)
fmt.Println(notify.Status)
fmt.Println(rec.Code)
}
// paymentNotifyHandler 是可复用的异步支付回调形态。
// notifier 为 alipay.Alipay、wechat.WechatPay 或 mock.Client。
//
// 仅 PaymentStatusSuccess 走支付入账;refunded 应走 RefundNotifier,不要当支付成功。
// 已验签且业务决定不处理的状态仍须写 SuccessResponse(支付宝 body=success / 微信 JSON SUCCESS),
// 否则平台会按失败持续重试。入账事务失败时不要写 SuccessResponse。
func paymentNotifyHandler(notifier payment.PaymentNotifier) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
notify, err := notifier.ParsePaymentNotification(r.Context(), r)
if err != nil {
http.Error(w, "bad notification", http.StatusBadRequest)
return
}
switch notify.Status {
case payment.PaymentStatusSuccess:
case payment.PaymentStatusRefunded:
default:
}
if err := notifier.SuccessResponse().WriteTo(w); err != nil {
return
}
})
}
Output: success 200
Index ¶
- Variables
- func ValidateContext(ctx context.Context) error
- func ValidateOrder(order *Order) error
- func ValidateOrderID(orderID string) error
- func ValidateRefundRequest(req *RefundRequest) error
- type JSAPIResult
- type NotifyResponse
- type Order
- type Payment
- type PaymentNotification
- type PaymentNotifier
- type PaymentResult
- type PaymentStatus
- type Provider
- type ProviderError
- type QueryResult
- type RefundNotification
- type RefundNotifier
- type RefundRequest
- type RefundResult
- type RefundStatus
- type WAPResult
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNotImplemented 表示适配器能力尚未实现。 // Deprecated: 内置支付宝和微信 adapter 已实现真实网关。 ErrNotImplemented = errors.New("payment adapter is not implemented") // ErrInvalidConfig 表示支付配置无效。 ErrInvalidConfig = errors.New("payment: invalid config") // ErrInvalidRequest 表示支付请求参数无效。 ErrInvalidRequest = errors.New("payment: invalid request") // ErrGateway 表示支付网关调用失败。 ErrGateway = errors.New("payment: gateway error") // ErrInvalidSignature 表示签名校验失败。 ErrInvalidSignature = errors.New("payment: invalid signature") // ErrUnknownStatus 表示服务商返回未知状态。 ErrUnknownStatus = errors.New("payment: unknown status") // ErrExpired 表示支付码已经过期。 ErrExpired = errors.New("payment: expired") // ErrUnsupportedClient 表示扫码客户端不受支持。 ErrUnsupportedClient = errors.New("payment: unsupported client") // ErrInvalidOAuthState 表示 OAuth state 校验失败。 ErrInvalidOAuthState = errors.New("payment: invalid oauth state") )
Functions ¶
func ValidateContext ¶ added in v0.28.0
ValidateContext 校验调用上下文。
func ValidateOrder ¶
ValidateOrder 校验支付订单。
Example ¶
ExampleValidateOrder 演示下单前统一校验。
package main
import (
"fmt"
"github.com/f2xme/gox/payment"
)
func main() {
err := payment.ValidateOrder(&payment.Order{
OrderID: "order-1001",
Amount: 9900,
Subject: "会员订阅",
NotifyURL: "https://merchant.example/payment/notify",
})
fmt.Println(err == nil)
}
Output: true
func ValidateRefundRequest ¶
func ValidateRefundRequest(req *RefundRequest) error
ValidateRefundRequest 校验退款请求。
Types ¶
type JSAPIResult ¶ added in v0.28.0
type JSAPIResult struct {
// AppID 是微信应用 ID。
AppID string `json:"appId"`
// Timestamp 是支付签名时间戳。
Timestamp string `json:"timeStamp"`
// NonceStr 是支付签名随机串。
NonceStr string `json:"nonceStr"`
// Package 是微信预支付包。
Package string `json:"package"`
// SignType 是签名算法。
SignType string `json:"signType"`
// PaySign 是支付签名。
PaySign string `json:"paySign"`
}
JSAPIResult 表示微信 JSAPI 调起支付参数。
type NotifyResponse ¶ added in v0.28.0
type NotifyResponse struct {
// StatusCode 是 HTTP 状态码。
StatusCode int
// ContentType 是响应 Content-Type。
ContentType string
// Body 是响应正文。
Body []byte
}
NotifyResponse 表示支付服务商要求的 HTTP 回执。
func (NotifyResponse) WriteTo ¶ added in v0.28.0
func (r NotifyResponse) WriteTo(w http.ResponseWriter) error
WriteTo 把回执写入 HTTP 响应。
type Order ¶
type Order struct {
// OrderID 是商户订单号,必须唯一。
OrderID string
// Amount 是支付金额,单位为分。
Amount int64
// Subject 是订单标题。
Subject string
// Description 是订单描述。
Description string
// NotifyURL 是异步支付通知地址。
NotifyURL string
// ReturnURL 是支付完成后的同步跳转地址。
ReturnURL string
// ExpireAt 是订单支付截止时间。
ExpireAt *time.Time
// Extra 保存服务商专有参数。
Extra map[string]any
}
Order 表示支付订单。
type Payment ¶
type Payment interface {
// Pay 使用给定订单发起支付。
Pay(ctx context.Context, order *Order) (*PaymentResult, error)
// Query 查询订单支付状态。
Query(ctx context.Context, orderID string) (*QueryResult, error)
// Refund 为已支付订单发起退款。
Refund(ctx context.Context, req *RefundRequest) (*RefundResult, error)
// Close 关闭未支付订单。
Close(ctx context.Context, orderID string) error
}
Payment 定义统一的支付操作接口。
type PaymentNotification ¶ added in v0.28.0
type PaymentNotification struct {
// Provider 是支付服务商。
Provider Provider
// OrderID 是商户订单号。
OrderID string
// TransactionID 是服务商交易流水号。
TransactionID string
// Status 是支付状态。
Status PaymentStatus
// Amount 是支付金额,单位为分。
Amount int64
// PaidAt 是支付完成时间。
PaidAt *time.Time
// Extra 保存复制后的服务商扩展字段。
Extra map[string]any
}
PaymentNotification 表示验签后的支付通知。
type PaymentNotifier ¶ added in v0.28.0
type PaymentNotifier interface {
// ParsePaymentNotification 解析并验证支付回调。
ParsePaymentNotification(ctx context.Context, req *http.Request) (*PaymentNotification, error)
// SuccessResponse 返回服务商要求的成功回执。
SuccessResponse() NotifyResponse
}
PaymentNotifier 定义支付回调解析能力。
type PaymentResult ¶
type PaymentResult struct {
// OrderID 是商户订单号。
OrderID string
// TransactionID 是支付服务商交易流水号。
TransactionID string
// PayURL 是二维码内容或收银台 URL。
PayURL string
// Extra 保存服务商专有支付参数。
Extra map[string]any
}
PaymentResult 表示发起支付后的结果。
type PaymentStatus ¶
type PaymentStatus string
PaymentStatus 表示支付状态。
const ( // PaymentStatusPending 表示支付待处理。 PaymentStatusPending PaymentStatus = "pending" // PaymentStatusSuccess 表示支付成功。 PaymentStatusSuccess PaymentStatus = "success" // PaymentStatusFailed 表示支付失败。 PaymentStatusFailed PaymentStatus = "failed" // PaymentStatusClosed 表示支付已关闭。 PaymentStatusClosed PaymentStatus = "closed" // PaymentStatusRefunded 表示支付已转入退款。 PaymentStatusRefunded PaymentStatus = "refunded" )
type Provider ¶ added in v0.28.0
type Provider string
Provider 表示支付服务提供商。
func ParseProvider ¶ added in v0.33.0
ParseProvider 规范化支付渠道名,供业务装配 switch 使用。
支持:mock / wechat / alipay(大小写不敏感,忽略首尾空白)。 空字符串视为 mock(本地默认)。未知渠道返回 ErrInvalidConfig。
生产代码可只依赖本包解析渠道,再分别装配 mock / wechat / alipay adapter, 无需为解析字符串而 import 测试用 mock 包。
Example ¶
ExampleParseProvider 演示按环境变量风格装配支付渠道。 本地默认 mock;生产显式 wechat / alipay。
package main
import (
"fmt"
"log"
"github.com/f2xme/gox/payment"
)
func main() {
for _, name := range []string{"", "mock", "wechat", "alipay", "wx"} {
provider, err := payment.ParseProvider(name)
if err != nil {
log.Fatal(err)
}
fmt.Println(name, "->", provider)
}
}
Output: -> mock mock -> mock wechat -> wechat alipay -> alipay wx -> wechat
type ProviderError ¶ added in v0.28.0
type ProviderError struct {
// Provider 是支付服务商。
Provider Provider
// Operation 是失败操作。
Operation string
// Code 是服务商错误码。
Code string
// Message 是可安全展示的错误摘要。
Message string
// Err 是原始原因。
Err error
}
ProviderError 表示支付服务商操作错误。
func (*ProviderError) Error ¶ added in v0.28.0
func (e *ProviderError) Error() string
Error 返回不包含凭据和原始请求体的错误描述。
func (*ProviderError) Unwrap ¶ added in v0.28.0
func (e *ProviderError) Unwrap() error
Unwrap 返回原始原因;未设置时返回 ErrGateway。
type QueryResult ¶
type QueryResult struct {
// OrderID 是商户订单号。
OrderID string
// TransactionID 是支付服务商交易流水号。
TransactionID string
// Status 是支付状态。
Status PaymentStatus
// Amount 是支付金额,单位为分。
Amount int64
// PaidAt 是支付完成时间。
PaidAt *time.Time
}
QueryResult 表示支付查询结果。
type RefundNotification ¶ added in v0.28.0
type RefundNotification struct {
// Provider 是支付服务商。
Provider Provider
// OrderID 是原商户订单号。
OrderID string
// TransactionID 是服务商交易流水号。
TransactionID string
// RefundID 是商户退款单号。
RefundID string
// ProviderRefundID 是服务商退款流水号。
ProviderRefundID string
// Status 是退款状态。
Status RefundStatus
// Amount 是退款金额,单位为分。
Amount int64
// RefundAt 是退款完成时间。
RefundAt *time.Time
// Extra 保存复制后的服务商扩展字段。
Extra map[string]any
}
RefundNotification 表示验签后的退款通知。
type RefundNotifier ¶ added in v0.28.0
type RefundNotifier interface {
// ParseRefundNotification 解析并验证退款回调。
ParseRefundNotification(ctx context.Context, req *http.Request) (*RefundNotification, error)
// SuccessResponse 返回服务商要求的成功回执。
SuccessResponse() NotifyResponse
}
RefundNotifier 定义退款回调解析能力。
type RefundRequest ¶
type RefundRequest struct {
// OrderID 是原商户订单号。
OrderID string
// RefundID 是商户退款单号,必须唯一。
RefundID string
// Amount 是退款金额,单位为分。
Amount int64
// OriginalAmount 是原订单总金额,单位为分。
OriginalAmount int64
// Reason 是退款原因。
Reason string
// NotifyURL 是异步退款通知地址。
NotifyURL string
}
RefundRequest 表示退款请求。
type RefundResult ¶
type RefundResult struct {
// RefundID 是商户退款单号。
RefundID string
// TransactionID 是支付服务商退款流水号。
TransactionID string
// Status 是退款状态。
Status RefundStatus
// RefundAt 是退款完成时间。
RefundAt *time.Time
}
RefundResult 表示退款结果。
type RefundStatus ¶
type RefundStatus string
RefundStatus 表示退款状态。
const ( // RefundStatusPending 表示退款待处理。 RefundStatusPending RefundStatus = "pending" // RefundStatusSuccess 表示退款成功。 RefundStatusSuccess RefundStatus = "success" // RefundStatusFailed 表示退款失败。 RefundStatusFailed RefundStatus = "failed" // RefundStatusClosed 表示退款已关闭。 RefundStatusClosed RefundStatus = "closed" )