fd5f56f7de
- 微信支付 V3 Native 渠道 (wechat.go):Native下单/回调AES-GCM解密验签/查单 - 支付宝:手机网站支付 wap.pay + 按 UA 自适应(PC page.pay扫码 / 手机拉App);qr_pay_mode=2 完整扫码收银台 - 业务对接:下单接口扩展(biz_system/biz_ref/return_url)+ HMAC 签名鉴权;支付成功 webhook 主动推送业务方 + 60s 重试 + BizNotifyLog - 通用多业务:config.biz 改 map,加业务只改配置(BIZ_<SYS>_SECRET/_CALLBACK_URL) - seedPlans:四档真实套餐 + promo_first_month(¥1) + test_liandiao(0.01),均挂 biz_code;/products 暴露 biz_code - 删除沙箱 pay.html;对接设计文档入 docs Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019UQmqWmV67sXGLrb3U1XXn
66 lines
1.9 KiB
Go
66 lines
1.9 KiB
Go
// Package channel 把不同支付渠道(支付宝/微信…)收口到统一接口。
|
|
// 上层业务只面向 Channel,新增渠道 = 加一个实现 + 一行注册,主流程不动。
|
|
package channel
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/wangjia/pay/internal/model"
|
|
)
|
|
|
|
// CreateReq 统一下单参数。
|
|
type CreateReq struct {
|
|
OutTradeNo string
|
|
Subject string
|
|
Amount string // 元,两位小数
|
|
ReturnURL string
|
|
NotifyURL string
|
|
IsMobile bool // 客户端是否手机浏览器:支付宝据此走手机网站支付(wap.pay 拉起 App) 而非电脑网站支付(page.pay 扫码)
|
|
}
|
|
|
|
// NotifyResult 异步通知验签解析后的统一结果。
|
|
type NotifyResult struct {
|
|
OutTradeNo string
|
|
TradeNo string
|
|
Amount string
|
|
BuyerLogonID string
|
|
Paid bool // 是否为支付成功状态
|
|
Raw string
|
|
}
|
|
|
|
// QueryResult 主动查单的统一结果。
|
|
type QueryResult struct {
|
|
Found bool
|
|
OutTradeNo string
|
|
TradeNo string
|
|
Amount string
|
|
Paid bool
|
|
}
|
|
|
|
// Channel 支付渠道统一接口。
|
|
type Channel interface {
|
|
Name() string
|
|
// PagePay 统一下单,返回收银台跳转 URL。
|
|
PagePay(ctx context.Context, req CreateReq) (payURL string, err error)
|
|
// PreCreate 扫码(当面付)预下单,返回二维码码串,由前端渲染成二维码供客户扫码付款。
|
|
PreCreate(ctx context.Context, req CreateReq) (qrCode string, err error)
|
|
// VerifyNotify 验签并解析异步通知(内部完成 ParseForm 与签名校验)。
|
|
VerifyNotify(ctx context.Context, r *http.Request) (*NotifyResult, error)
|
|
// Query 主动查单。
|
|
Query(ctx context.Context, outTradeNo string) (*QueryResult, error)
|
|
}
|
|
|
|
// Build 按商户配置构造渠道实例。
|
|
func Build(m *model.Merchant) (Channel, error) {
|
|
switch m.Channel {
|
|
case "alipay":
|
|
return newAlipay(m)
|
|
case "wechat":
|
|
return newWechat(m)
|
|
default:
|
|
return nil, fmt.Errorf("未知支付渠道: %q", m.Channel)
|
|
}
|
|
}
|