payment

package
v0.37.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 29, 2026 License: MIT Imports: 7 Imported by: 0

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

Examples

Constants

This section is empty.

Variables

View Source
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

func ValidateContext(ctx context.Context) error

ValidateContext 校验调用上下文。

func ValidateOrder

func ValidateOrder(order *Order) error

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 ValidateOrderID

func ValidateOrderID(orderID string) error

ValidateOrderID 校验商户订单号。

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 表示支付服务提供商。

const (
	// ProviderAlipay 表示支付宝。
	ProviderAlipay Provider = "alipay"
	// ProviderWechat 表示微信支付。
	ProviderWechat Provider = "wechat"
	// ProviderMock 表示测试用内存支付服务。
	ProviderMock Provider = "mock"
)

func ParseProvider added in v0.33.0

func ParseProvider(name string) (Provider, error)

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"
)

type WAPResult added in v0.28.0

type WAPResult struct {
	// URL 是服务商生成的完整收银台 URL。
	URL string
}

WAPResult 表示移动网页收银台结果。

Directories

Path Synopsis
adapter
mock
Package mock 提供并发安全、状态可控的内存支付测试实现。
Package mock 提供并发安全、状态可控的内存支付测试实现。
alipay module
onepay module
wechat module

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL