Help us shape the future of Go! Take the 2026 Go Developer Survey

protocol

package
v1.21.2 Latest Latest
Warning

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

Go to latest
Published: Oct 27, 2026 License: GPL-3.0 Imports: 27 Imported by: 0

Documentation

Overview

Package protocol implements EPay V1 and V2 using Go's standard library. NewClient selects V2 (RSA); NewV1Client selects V1 (MD5). Neither client automatically retries financial operations. Adapted from the local github.com/herta-network/epay SDK (2026-10-25). See docs/guide/epay.md for protocol coverage and backend integration boundaries.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func CanonicalString

func CanonicalString(params Params) (string, error)

CanonicalString follows the reference PHP SDK: retain "0", omit whitespace-only values, and preserve every byte of nonempty values. Do not URL-encode here.

func ParseAmount

func ParseAmount(value string) (int64, error)

ParseAmount converts a decimal yuan string into integer cents without floating point. Zero is valid for balances; payment/refund wrappers require a positive amount.

func V1CanonicalString

func V1CanonicalString(params Params) (string, error)

V1CanonicalString preserves whitespace-only values, unlike the V2 reference SDK. Only empty strings, sign and sign_type are omitted. Values are not encoded.

Types

type APIError

type APIError struct {
	Code     int
	Message  string
	Verified bool
}

APIError reports a V2 nonzero code. Verified=false means the error was unsigned: it is diagnostic information, not authenticated evidence of a financial outcome.

func (*APIError) Error

func (e *APIError) Error() string

type Client

type Client struct {
	// contains filtered or unexported fields
}

Client implements EPay V2. Use V1Client for the MD5 protocol.

func NewClient

func NewClient(config Config) (*Client, error)

func (*Client) Call

func (c *Client) Call(ctx context.Context, apiPath string, request Request) (*Response, error)

Call sends a single form POST and verifies successful responses before exposing their contents. Use this for merchant, transfer and complaint API extensions too.

func (*Client) CloseOrder

func (c *Client) CloseOrder(ctx context.Context, ref OrderRef) error

func (*Client) CreatePayment

func (c *Client) CreatePayment(ctx context.Context, request CreatePaymentRequest) (*PaymentResult, error)

func (*Client) PagePayment

func (c *Client) PagePayment(payment Payment) (*PageForm, error)

func (*Client) QueryOrder

func (c *Client) QueryOrder(ctx context.Context, ref OrderRef) (*Order, error)

func (*Client) QueryRefund

func (c *Client) QueryRefund(ctx context.Context, ref RefundRef) (*RefundStatus, error)

func (*Client) Refund

func (c *Client) Refund(ctx context.Context, request RefundRequest) (*RefundResult, error)

func (*Client) SignedParams

func (c *Client) SignedParams(params Params) (Params, error)

SignedParams injects pid, timestamp and sign_type. Caller-owned maps are not changed.

func (*Client) UploadComplaintImage

func (c *Client) UploadComplaintImage(ctx context.Context, complaintID string, data []byte) (string, error)

UploadComplaintImage uploads one PNG/JPEG (SDK limit: 5 MiB). File bytes are excluded from the protocol signature; HTTPS remains mandatory.

func (*Client) VerifyNotification

func (c *Client) VerifyNotification(values url.Values) (*Notification, error)

VerifyNotification validates authenticity, age, merchant and successful trade status. The caller must still match the stored order amount/type and atomically commit payment/delivery idempotently before replying with the literal "success".

type Config

type Config struct {
	BaseURL            string // Required HTTPS origin of the target EPay deployment.
	MerchantID         string
	MerchantPrivateKey []byte // Unencrypted PEM or bare Base64 DER.
	PlatformPublicKey  []byte // PEM or bare Base64 DER; never the merchant public key.
	HTTPClient         *http.Client
	MaxClockSkew       time.Duration // Zero selects the PHP SDK's 300-second window.
}

Config configures the EPay V2 RSA client. Use V1Config for shared-key V1.

type CreatePaymentRequest

type CreatePaymentRequest struct {
	Payment
	Method   string // web, jump, jsapi, app, scan, applet.
	Device   string // pc, mobile, qq, wechat, alipay, douyin.
	ClientIP string
}

func (CreatePaymentRequest) Validate

func (request CreatePaymentRequest) Validate() error

Validate checks a create before the application persists its attempt fence.

type HTTPError

type HTTPError struct{ StatusCode int }

func (*HTTPError) Error

func (e *HTTPError) Error() string

type Notification

type Notification struct {
	MerchantID  string
	TradeNo     string
	OutTradeNo  string
	APITradeNo  string
	Type        string
	Money       string
	Param       string
	Buyer       string
	CreatedAt   string
	CompletedAt string
	Fields      Params // Includes future scalar fields used for verification.
}

type Order

type Order struct {
	TradeNo     string      `json:"trade_no"`
	OutTradeNo  string      `json:"out_trade_no"`
	APITradeNo  string      `json:"api_trade_no"`
	Type        string      `json:"type"`
	Status      OrderStatus `json:"status"`
	Name        string      `json:"name"`
	Money       string      `json:"money"`
	RefundMoney string      `json:"refundmoney"`
	Param       string      `json:"param"`
	Buyer       string      `json:"buyer"`
	ClientIP    string      `json:"clientip"`
	CreatedAt   string      `json:"addtime"`
	CompletedAt string      `json:"endtime"`
}

type OrderRef

type OrderRef struct {
	TradeNo    string
	OutTradeNo string
}

type OrderStatus

type OrderStatus int
const (
	OrderUnpaid OrderStatus = iota
	OrderPaid
	OrderRefunded
	OrderFrozen
	OrderPreauthorized
)

type PageForm

type PageForm struct {
	Action string
	Fields url.Values
}

PageForm can be rendered with html/template. Fields are signed but not HTML-safe strings on their own; never concatenate them into HTML without escaping.

func (*PageForm) URL

func (f *PageForm) URL() string

type Params

type Params map[string]string

Params contains scalar protocol values before URL encoding. Money stays a string.

type Payment

type Payment struct {
	Type       string // alipay, wxpay, qqpay; empty permitted for page cashier / scan autodetection.
	OutTradeNo string
	NotifyURL  string
	ReturnURL  string
	Name       string
	Money      string // Yuan, e.g. "1.00". Never convert through float64.
	Param      string
	Extra      Params // merchant_id, channel_id, fee_mode, cert_no, cert_name, min_age, etc.
}

type PaymentResult

type PaymentResult struct {
	TradeNo string `json:"trade_no"`
	PayType string `json:"pay_type"`
	PayInfo string `json:"pay_info"`
}

type RefundRef

type RefundRef struct {
	RefundNo    string
	OutRefundNo string
}

type RefundRequest

type RefundRequest struct {
	OrderRef
	Money       string
	OutRefundNo string // Required by this SDK, although optional in the upstream API.
}

type RefundResult

type RefundResult struct {
	RefundNo    string `json:"refund_no"`
	OutRefundNo string `json:"out_refund_no"`
	TradeNo     string `json:"trade_no"`
	Money       string `json:"money"`
	ReduceMoney string `json:"reducemoney"`
}

type RefundStatus

type RefundStatus struct {
	RefundResult
	OutTradeNo string `json:"out_trade_no"`
	Status     int    `json:"status"` // 0 failed, 1 succeeded; separate from API code.
	CreatedAt  string `json:"addtime"`
}

type Request

type Request struct {
	Params Params
	Arrays map[string][]string
}

Request separates signed scalar fields from arrays, which the upstream protocol excludes from its signature. Arrays use PHP-compatible key[index] encoding.

type Response

type Response struct {
	UnsignedFields []string
	// contains filtered or unexported fields
}

Response is only returned after validating a code=0 response's signature and timestamp. Nested arrays/objects are NOT covered by this upstream protocol.

func (*Response) Decode

func (r *Response) Decode(destination any) error

func (*Response) JSON

func (r *Response) JSON() []byte

type V1APIError

type V1APIError struct {
	Code    int
	Message string
}

V1APIError identifies an API failure using the operation-specific success code. In particular code=0 fails ordinary V1 requests but succeeds for refunds.

func (*V1APIError) Error

func (e *V1APIError) Error() string

type V1Client

type V1Client struct {
	// contains filtered or unexported fields
}

V1Client implements EPay V1. It never falls back to V2 or retries requests.

func NewV1Client

func NewV1Client(config V1Config) (*V1Client, error)

func (*V1Client) CreatePayment

func (c *V1Client) CreatePayment(ctx context.Context, request V1CreatePaymentRequest) (*V1PaymentResult, error)

func (*V1Client) MerchantInfo

func (c *V1Client) MerchantInfo(ctx context.Context) (*V1Response, error)

MerchantInfo returns account data, including KEY on some deployments. Decode only the fields needed by the application instead of logging JSON().

func (*V1Client) Orders

func (c *V1Client) Orders(ctx context.Context, request V1OrdersRequest) (*V1Response, error)

func (*V1Client) PagePayment

func (c *V1Client) PagePayment(payment V1Payment) (*PageForm, error)

func (*V1Client) QueryOrder

func (c *V1Client) QueryOrder(ctx context.Context, ref OrderRef) (*V1Order, error)

func (*V1Client) Refund

func (c *V1Client) Refund(ctx context.Context, request V1RefundRequest) (*V1RefundResult, error)

func (*V1Client) Settlements

func (c *V1Client) Settlements(ctx context.Context) (*V1Response, error)

Settlements returns the deployment's settlement records in data. The reference document does not specify their row schema; use Decode with a site-specific type.

func (*V1Client) SignedParams

func (c *V1Client) SignedParams(params Params) (Params, error)

SignedParams adds the configured pid and MD5 signature without exposing KEY. V1 does not inject a timestamp. Caller-owned maps are not changed.

func (*V1Client) VerifyNotification

func (c *V1Client) VerifyNotification(values url.Values) (*Notification, error)

VerifyNotification validates V1's MD5 signature, merchant and successful trade status. There is no protocol timestamp/replay window; durable idempotency belongs to the business Store. All additional scalar fields participate in verification.

type V1Config

type V1Config struct {
	BaseURL    string // Required HTTPS origin of the target EPay deployment.
	MerchantID string
	Key        string // V1 shared merchant KEY, not a V2 RSA key.
	HTTPClient *http.Client
}

type V1CreatePaymentRequest

type V1CreatePaymentRequest struct {
	V1Payment
	ClientIP string
	Device   string // pc, mobile, qq, wechat, alipay, douyin, jump; empty uses the server default.
}

func (V1CreatePaymentRequest) Validate

func (request V1CreatePaymentRequest) Validate() error

Validate checks a create before the application persists its attempt fence.

type V1Order

type V1Order struct {
	MerchantID  string `json:"pid"`
	TradeNo     string `json:"trade_no"`
	OutTradeNo  string `json:"out_trade_no"`
	APITradeNo  string `json:"api_trade_no"`
	Type        string `json:"type"`
	Name        string `json:"name"`
	Money       string `json:"money"`
	Status      int    `json:"status"`
	Param       string `json:"param"`
	Buyer       string `json:"buyer"`
	CreatedAt   string `json:"addtime"`
	CompletedAt string `json:"endtime"`
}

V1Order deliberately uses a separate status field from V2's status enum. The V1 reference defines only 0 (unpaid) and 1 (paid).

func (*V1Order) UnmarshalJSON

func (o *V1Order) UnmarshalJSON(data []byte) error

UnmarshalJSON tolerates PHP's integer strings for pid/status, while preserving money and order identifiers as strings and rejecting duplicate top-level keys.

type V1OrdersRequest

type V1OrdersRequest struct {
	Page  int // Zero selects page 1.
	Limit int // Zero selects 20; maximum 50.
}

type V1Payment

type V1Payment struct {
	Type       string // Empty selects the cashier for PagePayment.
	OutTradeNo string
	NotifyURL  string
	ReturnURL  string
	Name       string
	Money      string
	Param      string
}

type V1PaymentResult

type V1PaymentResult struct {
	TradeNo   string `json:"trade_no"`
	PayURL    string `json:"payurl"`
	QRCode    string `json:"qrcode"`
	URLScheme string `json:"urlscheme"`
}

type V1RefundRequest

type V1RefundRequest struct {
	OrderRef
	Money string // V1 has no documented out_refund_no or refund query endpoint.
}

type V1RefundResult

type V1RefundResult struct {
	Message string `json:"msg"`
}

type V1Response

type V1Response struct {
	// contains filtered or unexported fields
}

V1Response has passed JSON and API success-code checks only. V1 does not define signed JSON responses; HTTPS authenticates transport. MerchantInfo may include the shared key, so never log the complete response.

func (*V1Response) Decode

func (r *V1Response) Decode(destination any) error

func (*V1Response) JSON

func (r *V1Response) JSON() []byte

Jump to

Keyboard shortcuts

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