native

package
v4.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 11, 2026 License: MIT Imports: 29 Imported by: 0

Documentation

Overview

Package native implements the payment package's v2 routes in the package itself, on the kernel's v2_payment_gateway, v2_payment_record and v2_payment tables adopted in place (kernel.storage.adopt). Legacy handlers and native routes share the tables, so a route can switch between them at any time. Responses are byte-compatible with the legacy handlers (internal/tests/paymentcompat).

The package owns the payment gateways and so holds their secrets (merchant keys, webhook secrets) in v2_payment_gateway; its administrator answers show them as a placeholder, as the kernel's do. An order is read through the kernel view kapi_order_billing_v1 (its buyer, total and status) only: a payment is created for the caller's own pending order and its exact total.

The four callback routes (the gateway callback, the x402 callback, the Stripe and PayPal webhooks) verify the provider's signature with the gateway's secrets, as the kernel's do (signatures.go). A paid callback then takes two steps (callbacks.go, docs/architecture/order-service.md):

  1. the package marks its payment record paid and adds it to its gateway's statistics, in one transaction on its own tables;
  2. the kernel applies the paid record to its order (KernelOrder.CompleteOrderPayment, kernel.order.complete.v1): it re-checks that the record pays the order, marks the order paid and completes it, in one transaction, once per trade number.

Every repeat of a callback whose record is already paid takes step 2 again, so a failure between the steps converges on the provider's next delivery; an order is never paid without a paid record. Without a bridge connection the callbacks have no native handler and stay legacy.

Index

Constants

View Source
const (
	CallbackRouteID      = "payment.callback"
	X402CallbackRouteID  = "payment.payment.x402.callback.post"
	StripeWebhookRouteID = "payment.payment.stripe.webhook.post"
	PayPalWebhookRouteID = "payment.payment.paypal.webhook.post"
)

The callback routes' ids.

View Source
const (
	PaymentStatusPending   = 0
	PaymentStatusPaid      = 1
	PaymentStatusCancelled = 2
	PaymentStatusRefunded  = 3
	PaymentStatusExpired   = 4
)

Payment statuses, as the kernel's model.PaymentStatus* constants.

Variables

This section is empty.

Functions

This section is empty.

Types

type BillingOrder

type BillingOrder struct {
	ID          uint `gorm:"primaryKey"`
	UserID      uint
	TotalAmount int64 // cents
	Status      int
}

BillingOrder is a row of kapi_order_billing_v1: what a payment needs of an order.

func (BillingOrder) TableName

func (BillingOrder) TableName() string

TableName is the kernel view.

type CreateGatewayRequest

type CreateGatewayRequest struct {
	Name        string  `json:"name" binding:"required"`
	Type        string  `json:"type" binding:"required,oneof=alipay wechat stripe usdt epay"`
	Icon        string  `json:"icon"`
	Config      any     `json:"config"`
	FeeRate     float64 `json:"fee_rate"`
	FeeFixed    float64 `json:"fee_fixed"`
	MinAmount   float64 `json:"min_amount"`
	MaxAmount   float64 `json:"max_amount"`
	Sort        int     `json:"sort"`
	Description string  `json:"description"`
}

CreateGatewayRequest is the kernel's request type of the same name; the name appears in binding errors.

type CreatePaymentRequest

type CreatePaymentRequest struct {
	GatewayID uint    `json:"gateway_id" binding:"required"`
	Amount    float64 `json:"amount" binding:"required,min=1"`
	OrderID   *uint   `json:"order_id"`
}

CreatePaymentRequest is the kernel's request type of the same name; the name appears in binding errors.

type OrderCompleter

type OrderCompleter interface {
	CompleteOrderPayment(ctx context.Context, in *kernelorderv1.CompleteOrderPaymentRequest, opts ...grpc.CallOption) (*kernelorderv1.CompleteOrderPaymentResponse, error)
}

OrderCompleter is the part of KernelOrder the callbacks call.

type Payment

type Payment struct {
	ID        uint      `gorm:"primaryKey" json:"id"`
	Name      string    `gorm:"size:100" json:"name"`
	Icon      *string   `gorm:"size:255" json:"icon"`
	Method    string    `gorm:"size:20" json:"method"`
	Provider  string    `gorm:"size:50" json:"provider"`
	Config    string    `gorm:"type:text" json:"config"`
	Notify    *string   `gorm:"size:255" json:"notify"`
	Sort      int       `gorm:"default:0" json:"sort"`
	Enable    int       `gorm:"default:1" json:"enable"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

Payment is a v2_payment row: the older payment method configuration the method list reads.

func (Payment) TableName

func (Payment) TableName() string

TableName is the adopted kernel table.

type PaymentChannel

type PaymentChannel struct {
	ID          uint    `json:"id"`
	Name        string  `json:"name"`
	Type        string  `json:"type"`
	Icon        string  `json:"icon"`
	MinAmount   float64 `json:"min_amount"`
	MaxAmount   float64 `json:"max_amount"`
	FeeRate     float64 `json:"fee_rate"`
	FeeFixed    float64 `json:"fee_fixed"`
	Description string  `json:"description"`
}

PaymentChannel is a gateway as users see it.

type PaymentGateway

type PaymentGateway struct {
	ID      uint   `gorm:"primaryKey" json:"id"`
	Name    string `gorm:"size:100;not null" json:"name"`
	Type    string `gorm:"size:20;not null" json:"type"` // alipay/wechat/stripe/usdt/epay
	Enabled bool   `gorm:"default:false" json:"enabled"`
	Icon    string `gorm:"size:255" json:"icon"`

	// Config is the gateway's JSON configuration, secrets included.
	Config string `gorm:"type:text" json:"config"`

	FeeRate  float64 `gorm:"default:0" json:"fee_rate"`
	FeeFixed float64 `gorm:"default:0" json:"fee_fixed"`

	MinAmount float64 `gorm:"default:1" json:"min_amount"`
	MaxAmount float64 `gorm:"default:10000" json:"max_amount"`

	Sort        int    `gorm:"default:0" json:"sort"`
	Description string `gorm:"size:500" json:"description"`

	TotalOrders int64   `json:"total_orders"`
	TotalAmount float64 `json:"total_amount"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

PaymentGateway is a v2_payment_gateway row. Its fields, tags and type name match the kernel model, so answers are the same.

func (PaymentGateway) TableName

func (PaymentGateway) TableName() string

TableName is the adopted kernel table.

type PaymentRecord

type PaymentRecord struct {
	ID             uint   `gorm:"primaryKey" json:"id"`
	TradeNo        string `gorm:"size:64;uniqueIndex" json:"trade_no"`
	GatewayID      uint   `json:"gateway_id"`
	GatewayType    string `json:"gateway_type"`
	GatewayTradeNo string `gorm:"size:100" json:"gateway_trade_no"`
	Provider       string `gorm:"size:50" json:"provider"`
	UserID         uint   `json:"user_id"`

	Amount       float64 `json:"amount"`
	FeeAmount    float64 `json:"fee_amount"`
	ActualAmount float64 `json:"actual_amount"`
	Currency     string  `gorm:"size:10;default:'CNY'" json:"currency"`

	Status      int        `json:"status"`
	PaidAt      *time.Time `json:"paid_at"`
	CancelledAt *time.Time `json:"cancelled_at"`
	RefundedAt  *time.Time `json:"refunded_at"`

	NotifyData string `gorm:"type:text" json:"notify_data"`
	ClientIP   string `gorm:"size:50" json:"client_ip"`

	TxHash        *string `gorm:"size:255" json:"tx_hash"`
	WalletAddress *string `gorm:"size:255" json:"wallet_address"`
	Network       *string `gorm:"size:50" json:"network"`

	OrderID *uint `json:"order_id"`

	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

PaymentRecord is a v2_payment_record row, with the kernel model's fields and tags.

func (PaymentRecord) TableName

func (PaymentRecord) TableName() string

TableName is the adopted kernel table.

type Service

type Service struct {
	// Open returns the package's storage connection, on which the adopted
	// tables and the granted view are visible.
	Open func(ctx context.Context) (*gorm.DB, error)
	// Orders is the kernel's KernelOrder; without it the callbacks have no
	// native handler and stay legacy.
	Orders OrderCompleter
	// HTTPClient calls PayPal's API to verify a webhook delivery; nil uses
	// a client with the kernel's 10-second timeout.
	HTTPClient *http.Client
	// Now defaults to time.Now.
	Now func() time.Time
}

Service holds what the native routes need.

func (*Service) AdminCreateGateway

func (s *Service) AdminCreateGateway(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

AdminCreateGateway is POST /api/v2/admin/payment/gateways. A new gateway is disabled; a secret sent as the placeholder is stored empty.

func (*Service) AdminDeleteGateway

func (s *Service) AdminDeleteGateway(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

AdminDeleteGateway is DELETE /api/v2/admin/payment/gateways/:id; deleting a gateway that does not exist succeeds.

func (*Service) AdminGateways

AdminGateways is GET /api/v2/admin/payment/gateways, secrets redacted.

func (*Service) AdminRecords

AdminRecords is GET /api/v2/admin/payment/records: newest first, filtered by status (a number or its name) and gateway type.

func (*Service) AdminStats

AdminStats is GET /api/v2/admin/payment/stats for the dates start to end (default: the last 30 days), as the kernel's GetPaymentStats and PaymentGatewayService.GetStats; failed sums count as zero, as there.

func (*Service) AdminToggleGateway

func (s *Service) AdminToggleGateway(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

AdminToggleGateway is POST /api/v2/admin/payment/gateways/:id/toggle: the state sent as "enabled", else the opposite of the current one. Only a gateway type with a live callback may be enabled.

func (*Service) AdminUpdateGateway

func (s *Service) AdminUpdateGateway(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

AdminUpdateGateway is PUT /api/v2/admin/payment/gateways/:id: the fields sent replace the stored ones, a secret sent as the placeholder keeps its stored value, and the whole row is saved.

func (*Service) FiatCreatePayment

func (s *Service) FiatCreatePayment(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

FiatCreatePayment is POST /api/v2/payment/fiat/create. The caller's order is validated as in the kernel, then a Stripe or PayPal payment is refused: neither provider's checkout is implemented, and an earlier version answered a simulated checkout link and stored a pending payment record that nothing could ever pay. No payment record is created.

func (*Service) Handlers

func (s *Service) Handlers() map[string]pluginhostsdk.NativeHandler

Handlers returns the native handlers by route id.

func (*Service) PayPalWebhook

PayPalWebhook is POST /api/v2/payment/paypal/webhook: a PayPal event that PayPal's API confirms. Only a completed capture pays (#74): it marks the payment paid when its amount and currency match, and completes its order; a denied capture or a processing order cancels the payment.

func (*Service) PaymentCallback

PaymentCallback is POST /api/v2/payment/callback/:type: a registered gateway's signed notification (EPay's, in its query) marks the payment paid and completes its order. It answers "success", or "fail" with 400.

func (*Service) PaymentMethods

PaymentMethods is GET /api/v2/payment/methods: the fixed method list, each enabled when an enabled v2_payment row names its provider.

func (*Service) PublicPaymentStatus

func (s *Service) PublicPaymentStatus(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

PublicPaymentStatus is GET /api/v2/payment/status/:trade_no, which needs no login: the trade number is the payment's reference.

func (*Service) StripeWebhook

StripeWebhook is POST /api/v2/payment/stripe/webhook: a Stripe event, signed with the gateway's webhook secret within five minutes. A completed checkout marks the payment paid and completes its order; an expired or failed one ends the payment.

func (*Service) UserChannels

UserChannels is GET /api/v2/user/payment/channels: the enabled gateways, without their configuration.

func (*Service) UserCreatePayment

func (s *Service) UserCreatePayment(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

UserCreatePayment is POST /api/v2/user/payment/create: a payment through an enabled gateway, within its limits, with its fee. A payment for an order pays the caller's own pending order in full.

func (*Service) UserPaymentStatus

func (s *Service) UserPaymentStatus(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

UserPaymentStatus is GET /api/v2/user/payment/status/:trade_no for the caller's own payment; another user's is answered as missing.

func (*Service) UserRecords

UserRecords is GET /api/v2/user/payment/records: the caller's records, newest first. The page is not clamped, as in the kernel.

func (*Service) X402Callback

X402Callback is POST /api/v2/payment/x402/callback: the confirmation service's signed report on a crypto payment. A confirmed transfer of the payment's token and amount marks it paid and completes its order; a failed one cancels the payment.

func (*Service) X402CheckPayment

func (s *Service) X402CheckPayment(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

X402CheckPayment is GET /api/v2/payment/x402/check/:id: the caller's payment by trade number, else by numeric id; another user's is answered as missing.

func (*Service) X402CreatePayment

func (s *Service) X402CreatePayment(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)

X402CreatePayment is POST /api/v2/payment/x402/create: a test-chain crypto payment of the caller's pending order, to a generated address.

type UpdateGatewayRequest

type UpdateGatewayRequest struct {
	Name        string   `json:"name"`
	Type        string   `json:"type" binding:"omitempty,oneof=alipay wechat stripe usdt epay"`
	Icon        string   `json:"icon"`
	Config      any      `json:"config"`
	FeeRate     *float64 `json:"fee_rate"`
	FeeFixed    *float64 `json:"fee_fixed"`
	MinAmount   *float64 `json:"min_amount"`
	MaxAmount   *float64 `json:"max_amount"`
	Sort        *int     `json:"sort"`
	Description string   `json:"description"`
}

UpdateGatewayRequest is the kernel's request type of the same name.

Jump to

Keyboard shortcuts

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