kode/pays

Kode Pays - 企业级多平台聚合支付 SDK,支持微信、支付宝、云闪付、抖音支付、美团支付、京东支付、快手支付、QQ 支付、支付宝国际版、PayPal、Stripe、Square、Adyen、Amazon Pay、Klarna、Wise、Revolut、Payoneer、Apple Pay、Google Pay、HitPay、Xendit 等国内外主流支付渠道

Maintainers

Package info

github.com/kodephp/pays

pkg:composer/kode/pays

Transparency log

Statistics

Installs: 84

Dependents: 0

Suggesters: 1

Stars: 1

Open Issues: 0

1.45.0 2026-08-10 05:48 UTC

README

Kode Pays 是一个面向 PHP 8.3+ 的企业级多平台聚合支付 SDK,支持微信、支付宝、云闪付、抖音支付、美团支付、京东支付、快手支付、QQ 支付、支付宝国际版、PayPal、Stripe、Square、Adyen、Amazon Pay、Klarna、Wise、Revolut、Payoneer、Apple Pay、Google Pay、HitPay、Xendit 等国内外主流支付渠道。采用事件驱动、管道中间件、门面模式等现代架构设计,让开发者能够快速、安全、可扩展地接入各种支付能力。

特性

  • 多平台支持:微信、支付宝、云闪付、抖音支付、美团支付、京东支付、快手支付、QQ 支付、支付宝国际版、PayPal、Stripe、Square、Adyen、Amazon Pay、Klarna、Wise、Revolut、Payoneer、Apple Pay、Google Pay、HitPay、Xendit、聚合支付
  • 统一接口:所有网关实现同一接口,切换渠道对业务代码完全无感知
  • 聚合路由:支持多渠道配置,自动优先级路由和失败切换
  • 门面模式Pay::wechat($config) 一行代码创建网关
  • 沙箱管理:全局/按网关独立控制沙箱环境,测试不扣真实资金
  • 事件驱动:支付生命周期各阶段触发事件,解耦日志/监控/通知
  • 管道中间件:请求参数通过中间件栈处理,支持签名、日志、加密、限流等
  • 熔断保护CircuitBreakerMiddleware 对不稳定通道进行故障隔离(closed/open/half-open 三态)
  • 类型安全:充分利用 PHP 8.3+ 特性(readonlymatchenumtyped class constants
  • 枚举与值对象:内置 Currency / TradeStatus / TradeType 枚举与不可变 Money 金额对象(含 allocate 分账、distribute 均分),消除状态字符串与浮点金额隐患
  • 响应类型化访问器PayResponse 直接提供 getAmountMoney() / getCurrencyEnum() / getTradeTypeEnum() 等类型安全访问器,免去手工解析与币种换算
  • 分账能力完善ProfitSharingPlugin 提供微信/支付宝/Stripe 的完整分账 API 集合(增删接收方、分账、回退、查询、配置查询、解冻),配套 Receiver 值对象(Money 承载金额)与 Result 归一化结果
  • 统一收款码UnifiedQrRouter 一码聚合多支付通道,QrEntry 不可变值对象承载入口(状态机 pending→ordered→paid/closed)、PayResponse::getQrContent() 归一化各网关二维码字段(qr_code/code_url/payment_link/pay_url)
  • 统一入口与平台清单GatewayManifest 把各平台「域名、签名方案、能力开关、区域」集中声明到一个 registry,调用方只查清单即可;Pay::call($gateway, $method, ...) 一个方法即可调用任意已接入平台的任意方法(含各平台特色方法),Pay::extend() 一次登记即可新增平台
  • 回调安全校验NotifyGuard 统一拦截异步通知(必填字段、签名字段、时间戳/nonce 防重放),Pay::verify() 先过安全校验再走平台级验签,杜绝畸形/重放数据直达业务
  • 异常细分:6 种具体异常子类,便于精确捕获和差异化处理
  • 中文注释:所有代码和文档均为中文,降低国内开发者学习成本
  • 生态兼容:预留 kode 系列扩展点(二维码、协程、缓存、数据库等)

环境要求

  • PHP >= 8.3
  • ext-json
  • ext-openssl
  • Composer

安装

composer require kode/pays

可选扩展(推荐)

# 支付二维码生成
composer require kode/tools

# 依赖注入容器
composer require kode/di

# 订单缓存与分布式锁
composer require kode/cache

# 订单持久化与分库分表
composer require kode/database

# 协程支持
composer require kode/fibers

# 多进程支持
composer require kode/process

# 多线程支持
composer require kode/parallel

# 限流保护
composer require kode/limiting

# 异常处理增强
composer require kode/exception

# 门面模式增强
composer require kode/facade

# 日志记录(PSR-3 兼容)
composer require monolog/monolog

快速开始

门面模式快速接入

<?php

use Kode\Pays\Facade\Pay;

// 微信支付
$wechat = Pay::wechat([
    'app_id'  => 'wx1234567890abcdef',
    'mch_id'  => '1234567890',
    'api_key' => 'your-api-key-here',
]);

$result = $wechat->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_fee'    => 100,
    'body'         => '测试商品',
    'trade_type'   => 'NATIVE',
    'notify_url'   => 'https://your-domain.com/notify/wechat',
]);

// 获取支付二维码链接
$codeUrl = $result['code_url'] ?? '';

统一入口:一个方法调用任意平台

不论接入的是哪个平台、调用的是标准方法还是各平台「特色方法」,都可以通过统一入口 Pay::call() 完成,无需关心具体网关类:

<?php

use Kode\Pays\Facade\Pay;

// 1) 统一下单:标准方法也能用一个入口发起
$result = Pay::call('wechat', 'createOrder', [
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_fee'    => 100,
    'body'         => '测试商品',
    'trade_type'   => 'NATIVE',
]);

// 2) 语义化快捷方法(等价于 call)
$result = Pay::createOrder('alipay', [/* ... */]);
$result = Pay::refund('wechat', [/* ... */]);

// 3) 各平台「特色方法」同样可直接调用
$result = Pay::call('wechat', 'someWechatSpecificMethod', $arg1, $arg2);

// 4) 拿到强类型实例,自由调用
$wechat = Pay::gateway('wechat', $config);
$wechat->createOrder([/* ... */]);

// 5) 安全校验回调(先过 NotifyGuard 再走平台级验签)
$ok = Pay::verify('wechat', $_POST, [
    'timestamp' => (int) ($_POST['timestamp'] ?? 0),
    'nonce'     => $_POST['nonce'] ?? null,
    'seen_nonces' => $alreadySeenNonces, // 由调用方维护,防重放
]);

// 6) 查询平台能力 / 域名(无需创建实例)
if (Pay::supports('wechat', \Kode\Pays\Core\GatewayManifest::CAP_PROFIT_SHARING)) {
    // 该平台支持分账
}
$domain = Pay::baseUrl('wechat'); // 生产域名

新增一个支付平台也只需「一次登记」即可纳入统一入口:

<?php

use Kode\Pays\Core\GatewayManifest;
use Kode\Pays\Facade\Pay;

Pay::extend('mypay', [
    'label'         => 'MyPay',
    'region'        => GatewayManifest::REGION_DOMESTIC,
    'signature'     => GatewayManifest::SIGN_MD5,
    'base_url'      => 'https://api.mypay.com/',
    'sandbox_url'   => 'https://sandbox.mypay.com/',
    'capabilities'  => [GatewayManifest::CAP_PROFIT_SHARING => true],
], MyPayGateway::class, MyPayConfig::class);

// 登记后即可通过统一入口调用
Pay::createOrder('mypay', [/* ... */]);

支付宝

<?php

use Kode\Pays\Facade\Pay;

$alipay = Pay::alipay([
    'app_id'      => '2024XXXXXXXXXXXX',
    'private_key' => '-----BEGIN RSA PRIVATE KEY-----\n...',
    'public_key'  => '-----BEGIN PUBLIC KEY-----\n...',
]);

$result = $alipay->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_amount' => '0.01',
    'subject'      => '测试商品',
    'notify_url'   => 'https://your-domain.com/notify/alipay',
    'return_url'   => 'https://your-domain.com/return',
]);

// 跳转到支付宝收银台
header('Location: ' . $result['url']);

PayPal

<?php

use Kode\Pays\Facade\Pay;

$paypal = Pay::paypal([
    'client_id'     => 'YOUR_CLIENT_ID',
    'client_secret' => 'YOUR_CLIENT_SECRET',
]);

$result = $paypal->createOrder([
    'intent' => 'CAPTURE',
    'purchase_units' => [
        [
            'amount' => [
                'currency_code' => 'USD',
                'value' => '10.00',
            ],
        ],
    ],
]);

Stripe

<?php

use Kode\Pays\Facade\Pay;

$stripe = Pay::stripe([
    'secret_key' => 'sk_test_...',
]);

// PaymentIntent 方式
$result = $stripe->createOrder([
    'amount'   => 1000,
    'currency' => 'usd',
    'metadata' => ['order_id' => 'ORDER_001'],
]);

// Checkout Session 方式
$session = $stripe->createCheckoutSession([
    'line_items' => [[
        'price_data' => [
            'currency' => 'usd',
            'product_data' => ['name' => '测试商品'],
            'unit_amount'  => 1000,
        ],
        'quantity' => 1,
    ]],
    'mode' => 'payment',
    'success_url' => 'https://your-domain.com/success',
    'cancel_url'  => 'https://your-domain.com/cancel',
]);

Square

<?php

use Kode\Pays\Facade\Pay;

$square = Pay::square([
    'access_token' => 'EAAA...',
    'environment'  => 'sandbox',
]);

$result = $square->createOrder([
    'amount'   => 100,
    'currency' => 'USD',
    'note'     => '测试商品',
    'source_id' => 'cnon:card-nonce-ok',
]);

Adyen

<?php

use Kode\Pays\Facade\Pay;

$adyen = Pay::adyen([
    'api_key'        => 'AQE1hmfxJ...',
    'merchant_account' => 'YourMerchantAccount',
    'environment'    => 'test',
]);

$result = $adyen->createOrder([
    'amount' => [
        'value'    => 1000,
        'currency' => 'USD',
    ],
    'reference'       => 'ORDER_001',
    'returnUrl'       => 'https://your-domain.com/return',
    'countryCode'     => 'US',
]);

美团支付

<?php

use Kode\Pays\Facade\Pay;

$meituan = Pay::meituan([
    'app_id'      => 'mt123456',
    'app_secret'  => 'your-app-secret',
    'merchant_id' => 'M123456',
]);

$result = $meituan->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_fee'    => 100,
    'body'         => '美团外卖订单',
    'notify_url'   => 'https://your-domain.com/notify/meituan',
    'trade_type'   => 'APP',
]);

$payUrl = $result['pay_url'] ?? '';

京东支付

<?php

use Kode\Pays\Facade\Pay;

$jd = Pay::jd([
    'merchant_no' => 'JD123456',
    'des_key'     => 'your-des-key',
    'md5_key'     => 'your-md5-key',
]);

$result = $jd->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_amount' => '10.00',
    'subject'      => '京东商品',
    'notify_url'   => 'https://your-domain.com/notify/jd',
    'trade_type'   => 'APP',
]);

快手支付

<?php

use Kode\Pays\Facade\Pay;

$kuaishou = Pay::kuaishou([
    'app_id'      => 'ks123456',
    'app_secret'  => 'your-app-secret',
    'merchant_id' => 'M123456',
]);

$result = $kuaishou->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_amount' => 100,
    'subject'      => '快手小店商品',
    'notify_url'   => 'https://your-domain.com/notify/kuaishou',
    'trade_type'   => 'MINI_PROGRAM',
]);

Apple Pay

<?php

use Kode\Pays\Facade\Pay;

$apple = Pay::apple([
    'merchant_identifier'      => 'merchant.com.yourdomain',
    'merchant_certificate'     => file_get_contents('/path/to/cert.pem'),
    'merchant_certificate_key' => file_get_contents('/path/to/key.pem'),
    'apple_pay_merchant_id'    => 'your_apple_merchant_id',
    'domain_name'              => 'your-domain.com',
]);

$result = $apple->createOrder([
    'out_trade_no'  => 'ORDER_' . date('YmdHis'),
    'total_amount'  => '10.00',
    'currency'      => 'CNY',
    'payment_token' => $frontendPaymentToken,
]);

Google Pay

<?php

use Kode\Pays\Facade\Pay;

$google = Pay::google([
    'merchant_id'         => 'BCR2DN4T7ZTLKJ3H',
    'merchant_name'       => 'Your Store Name',
    'gateway_merchant_id' => 'your_gateway_merchant_id',
    'environment'         => 'TEST',
]);

$result = $google->createOrder([
    'out_trade_no'  => 'ORDER_' . date('YmdHis'),
    'total_amount'  => '10.00',
    'currency'      => 'USD',
    'payment_token' => $frontendPaymentToken,
]);

Amazon Pay

<?php

use Kode\Pays\Facade\Pay;

$amazon = Pay::amazon([
    'merchant_id' => 'A2QEXAMPLE123',
    'access_key'  => 'AKIA...',
    'secret_key'  => 'your-secret-key',
    'client_id'   => 'amzn1.application-oa2-client.xxx',
    'region'      => 'na',
]);

$result = $amazon->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_amount' => '10.00',
    'currency'     => 'USD',
    'amazon_order_reference_id' => 'S01-1234567-1234567',
]);

Klarna

<?php

use Kode\Pays\Facade\Pay;

$klarna = Pay::klarna([
    'username' => 'PK12345_abc123...',
    'password' => 'your-api-password',
    'region'   => 'eu',
]);

$result = $klarna->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_amount' => 100.00,
    'currency'     => 'EUR',
    'country'      => 'DE',
    'items' => [
        ['name' => 'T-Shirt', 'quantity' => 1, 'price' => 50.00],
        ['name' => 'Jeans', 'quantity' => 1, 'price' => 50.00],
    ],
]);

支付宝国际版

<?php

use Kode\Pays\Facade\Pay;

$alipayGlobal = Pay::alipayGlobal([
    'app_id'      => '2024xxxxxx',
    'private_key' => file_get_contents('/path/to/private_key.pem'),
    'public_key'  => file_get_contents('/path/to/public_key.pem'),
    'sign_type'   => 'RSA2',
]);

$result = $alipayGlobal->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_amount' => '10.00',
    'currency'     => 'USD',
    'subject'      => '跨境商品',
    'notify_url'   => 'https://your-domain.com/notify/alipay_global',
]);

Wise

<?php

use Kode\Pays\Facade\Pay;

$wise = Pay::wise([
    'api_key'    => 'your-api-key',
    'profile_id' => '12345678',
]);

$result = $wise->createOrder([
    'out_trade_no'    => 'ORDER_' . date('YmdHis'),
    'source_currency' => 'GBP',
    'target_currency' => 'EUR',
    'amount'          => 100.00,
    'recipient'       => [
        'currency' => 'EUR',
        'type'     => 'iban',
        'accountHolderName' => 'John Doe',
        'details'  => [
            'iban' => 'DE89370400440532013000',
        ],
    ],
]);

Revolut

<?php

use Kode\Pays\Facade\Pay;

$revolut = Pay::revolut([
    'api_key'     => 'your-api-key',
    'merchant_id' => 'your-merchant-id',
]);

$result = $revolut->createOrder([
    'out_trade_no'    => 'ORDER_' . date('YmdHis'),
    'total_amount'    => 10.00,
    'currency'        => 'EUR',
    'description'     => 'Revolut 商品',
    'customer_email'  => 'customer@example.com',
    'redirect_url'    => 'https://your-domain.com/success',
]);

Payoneer

<?php

use Kode\Pays\Facade\Pay;

$payoneer = Pay::payoneer([
    'api_key'    => 'your-api-key',
    'api_secret' => 'your-api-secret',
    'program_id' => 'your-program-id',
]);

$result = $payoneer->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'amount'       => 100.00,
    'currency'     => 'USD',
    'payee_id'     => 'payee_123456',
    'description'  => 'Payoneer 付款',
]);

聚合支付(多渠道自动切换)

<?php

use Kode\Pays\Facade\Pay;

$aggregate = Pay::aggregate([
    'channels' => [
        [
            'gateway'  => 'wechat',
            'priority' => 1,
            'config'   => [
                'app_id'  => 'wx123456',
                'mch_id'  => '123456',
                'api_key' => 'key',
            ],
        ],
        [
            'gateway'  => 'alipay',
            'priority' => 2,
            'config'   => [
                'app_id'      => '2024...',
                'private_key' => '...',
                'public_key'  => '...',
            ],
        ],
    ],
]);

// 自动选择可用渠道
$result = $aggregate->createOrder([
    'out_trade_no' => 'ORDER_001',
    'total_fee'    => 100,
    'body'         => '测试商品',
]);

// 返回结果包含实际使用的渠道
$channel = $result['_channel']; // wechat 或 alipay

沙箱环境管理

<?php

use Kode\Pays\Core\SandboxManager;
use Kode\Pays\Facade\Pay;

// 全局开启沙箱(所有网关)
SandboxManager::enableGlobal();

// 仅对微信开启沙箱
SandboxManager::enable('wechat');

// 检查当前环境
if (SandboxManager::isSandbox('wechat')) {
    echo '当前为微信沙箱环境' . PHP_EOL;
}

// 获取沙箱 URL
$url = SandboxManager::getBaseUrl('wechat');

// 沙箱模式下创建订单不会扣真实资金
$wechat = Pay::wechat([...]);
$result = $wechat->createOrder([...]);

事件系统

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Event\Events;

// 注册支付成功监听器
Pay::on(Events::PAYMENT_SUCCESS, function (array $payload) {
    $orderId = $payload['order_id'];
    $amount  = $payload['amount'];

    // 发送支付成功通知(邮件/短信/站内信)
    // 与支付逻辑完全解耦
});

// 注册异常监听器
Pay::on(Events::EXCEPTION_OCCURRED, function (array $payload) {
    $exception = $payload['exception'];

    // 上报监控(Sentry/钉钉/企业微信)
});

// 注册请求日志监听器
Pay::on(Events::REQUEST_SENDING, function (array $payload) {
    // 记录请求参数
    return $payload;
});

异步通知处理

<?php

use Kode\Pays\Facade\Pay;

// 获取通知数据
$data = $_POST;

// 创建对应网关
$gateway = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

// 验证签名
if ($gateway->verifyNotify($data)) {
    // 处理业务逻辑
    $orderId = $data['out_trade_no'];

    // 返回成功响应
    echo '<xml><return_code><![CDATA[SUCCESS]]></return_code></xml>';
} else {
    echo '<xml><return_code><![CDATA[FAIL]]></return_code></xml>';
}

协程并发处理(需安装 kode/fibers 或 Swoole)

<?php

use Kode\Pays\Async\AsyncNotifyHandler;
use Kode\Pays\Facade\Pay;

$handler = new AsyncNotifyHandler();

// 批量并发处理通知
$tasks = [
    ['gateway' => Pay::wechat($config1), 'data' => $notify1],
    ['gateway' => Pay::alipay($config2), 'data' => $notify2],
    ['gateway' => Pay::paypal($config3), 'data' => $notify3],
];

$results = $handler->handleConcurrent($tasks, function ($data) {
    // 处理业务逻辑
    return true;
});

分账插件

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Plugin\ProfitSharingPlugin;

$wechat = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

$plugin = new ProfitSharingPlugin($wechat);

// 添加分账接收方
$plugin->addReceiver([
    'type'    => 'MERCHANT_ID',
    'account' => '1234567890',
    'name'    => '供应商A',
]);

// 创建分账
$result = $plugin->create([
    'transaction_id' => '4200000000000000',
    'out_order_no'   => 'SHARE_' . date('YmdHis'),
    'receivers'      => [
        ['type' => 'MERCHANT_ID', 'account' => '1234567890', 'amount' => 100, 'description' => '供应商分账'],
        ['type' => 'PERSONAL_OPENID', 'account' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o', 'amount' => 50, 'description' => '推广者分账'],
    ],
]);

// 查询分账结果
$result = $plugin->query('SHARE_20240425000001');

// 分账回退
$result = $plugin->return([
    'out_order_no'  => 'SHARE_20240425000001',
    'out_return_no' => 'RETURN_' . date('YmdHis'),
    'return_amount' => 100,
]);

// 解冻剩余资金
$result = $plugin->unfreeze('4200000000000000');

订阅支付插件

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Plugin\SubscriptionPlugin;

$stripe = Pay::stripe(['secret_key' => 'sk_test_...']);
$plugin = new SubscriptionPlugin($stripe);

// 创建订阅计划
$plan = $plugin->createPlan([
    'name'     => '月度会员',
    'amount'   => 9900,
    'currency' => 'usd',
    'interval' => 'month',
]);

// 创建订阅
$subscription = $plugin->createSubscription([
    'customer_id' => 'cus_xxx',
    'plan_id'     => $plan['id'],
]);

// 暂停订阅
$plugin->pauseSubscription($subscription['id']);

// 恢复订阅
$plugin->resumeSubscription($subscription['id']);

// 取消订阅
$plugin->cancelSubscription($subscription['id']);

支持 Stripe、PayPal、Square(完整六方法)以及支付宝周期扣款、微信支付 V2 委托代扣、 Adyen Recurring(受平台端点限制不支持暂停 / 恢复)。国内平台的 createSubscription 返回签约跳转链接,签约后需商户按周期主动扣款:

// 支付宝周期扣款:签约 → 代扣(金额单位为元)
$alipay = Pay::alipay([...]);
$sign = Pay::call('alipay', 'createSubscription', [
    'customer_id' => 'AGREEMENT_001',   // 商户侧协议号
    'plan_id'     => 'plan_monthly',
    'amount'      => 19.9,
    'interval'    => 'month',
    'notify_url'  => 'https://example.com/sign-notify',
]);
// $sign['url'] 引导用户完成签约,回调拿到 agreement_no 后按周期扣款
Pay::call('alipay', 'payWithAgreement', [
    'out_trade_no' => 'SUB_202608_001',
    'total_amount' => 19.9,
    'subject'      => '月度会员续费',
    'agreement_no' => '20260810000000000001',
]);

// 微信委托代扣:模板 ID 需在商户平台后台配置
Pay::call('wechat', 'createSubscription', [
    'customer_id' => 'CONTRACT_001',
    'plan_id'     => '100001',
    'notify_url'  => 'https://example.com/sign-notify',
]);

详见 订阅能力设计

转账插件

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Plugin\TransferPlugin;

$wechat = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

$plugin = new TransferPlugin($wechat);

// 单笔转账到零钱
$result = $plugin->single([
    'out_biz_no'  => 'TRANSFER_' . date('YmdHis'),
    'amount'      => 100,
    'recipient'   => ['type' => 'openid', 'account' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o', 'name' => '张三'],
    'description' => '佣金提现',
]);

// 批量转账
$result = $plugin->batch([
    'out_biz_no' => 'BATCH_' . date('YmdHis'),
    'transfer_detail_list' => [
        ['out_detail_no' => 'D001', 'amount' => 100, 'recipient' => ['account' => 'openid1', 'name' => '张三'], 'remark' => '佣金'],
        ['out_detail_no' => 'D002', 'amount' => 200, 'recipient' => ['account' => 'openid2', 'name' => '李四'], 'remark' => '奖励'],
    ],
]);

// 查询转账结果
$result = $plugin->query('TRANSFER_20240425000001');

// 获取电子回单
$result = $plugin->receipt('TRANSFER_20240425000001');

对账插件

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Plugin\ReconciliationPlugin;

$wechat = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

$plugin = new ReconciliationPlugin($wechat);

// 下载交易对账单
$bill = $plugin->downloadBill([
    'bill_date' => '20240425',
    'bill_type' => 'ALL',
]);

// 下载资金账单
$fundFlow = $plugin->downloadFundFlow([
    'bill_date' => '20240425',
    'account_type' => 'Basic',
]);

// 解析对账单
$records = $plugin->parseBill($rawCsvData);

// 系统订单与对账单差异比对
$diff = $plugin->diff($systemOrders, $records);
if ($diff['is_consistent']) {
    echo '对账一致';
} else {
    print_r($diff['only_in_system']);
    print_r($diff['amount_mismatch']);
}

退款插件

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Plugin\RefundPlugin;

$wechat = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

$plugin = new RefundPlugin($wechat);

// 申请退款
$result = $plugin->apply([
    'out_trade_no'  => 'ORDER_001',
    'out_refund_no' => 'REFUND_001',
    'total_fee'     => 100,
    'refund_fee'    => 50,
    'refund_desc'   => '商品质量问题',
]);

// 查询退款
$result = $plugin->query('REFUND_001');

红包插件

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Plugin\RedPacketPlugin;

$wechat = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

$plugin = new RedPacketPlugin($wechat);

// 发放普通红包
$result = $plugin->send([
    'mch_billno'   => 'REDPACK_' . date('YmdHis'),
    'send_name'    => '某某公司',
    're_openid'    => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
    'total_amount' => 100,
    'total_num'    => 1,
    'wishing'      => '恭喜发财',
    'act_name'     => '新年活动',
    'remark'       => '参与活动领取红包',
]);

// 发放裂变红包
$result = $plugin->group([
    'mch_billno'   => 'GROUP_' . date('YmdHis'),
    'send_name'    => '某某公司',
    're_openid'    => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
    'total_amount' => 300,
    'total_num'    => 3,
    'wishing'      => '裂变红包',
    'act_name'     => '分享活动',
    'remark'       => '分享给好友领取',
]);

// 查询红包记录
$result = $plugin->query('REDPACK_20240425000001');

个人收款插件

支持网关:微信(wechat)、微信 V3(wechat_v3)、支付宝(alipay)、云闪付(unionpay)、Stripe(stripe)、PayPal(paypal)、Square(square)、Revolut(revolut)。 各平台个人收款实现路径不同:微信/支付宝走原生收款码;云闪付走后台交易二维码消费 + 代付;PayPal 走发票(Invoicing)二维码 + Payouts 提现;Square 走 Online Checkout Quick Pay;Revolut 走 Merchant Orders + 复用转账接口;Stripe 走 Checkout Session + Payouts 提现。 注意:云闪付无交易列表接口,queryRecords 需逐笔传入 out_trade_no;Square 无主动提现能力,withdraw 会抛出「无此方法」异常。

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Plugin\PersonalReceivePlugin;

$wechat = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

$plugin = new PersonalReceivePlugin($wechat);

// 生成个人收款码
$result = $plugin->createQrCode([
    'amount'      => 100,
    'description' => '商品付款',
    'attach'      => ['product_id' => '123'],
]);

// 查询收款记录
$records = $plugin->queryRecords([
    'start_time' => '2024-04-01 00:00:00',
    'end_time'   => '2024-04-25 23:59:59',
]);

// 提现到银行卡
$result = $plugin->withdraw([
    'amount'       => 5000,
    'bank_card_no' => '622202************',
    'real_name'    => '张三',
    'out_biz_no'   => 'WITHDRAW_' . date('YmdHis'),
]);

// 查询提现结果
$result = $plugin->queryWithdraw('WITHDRAW_20240425000001');

统一收款码路由器

针对多用户扫码场景,提供一个二维码兼容多支付通道的聚合收款方案:

<?php

use Kode\Pays\Core\OrderMonitorDaemon;
use Kode\Pays\Core\UnifiedQrRouter;

// 1. 配置各通道网关,生成统一入口码(一个二维码兼容多通道)
$router = new UnifiedQrRouter([
    'wechat' => ['app_id' => 'wx1', 'mch_id' => 'm1', 'api_key' => 'k1'],
    'alipay' => ['app_id' => 'a1', 'private_key' => '...', 'alipay_public_key' => '...'],
]);

$entry = $router->createEntry(['wechat', 'alipay'], 100, '商品付款');
// $entry['qr_content'] 用于渲染二维码图片

// 2. 用户扫码进入 H5 选择通道后,路由下单生成动态订单码
$order = $router->route($entry['router_id'], 'wechat');
// $order['code_url'] 是微信 Native 扫码支付链接

// 3. 注册后台监控,进程内持续抓取收款状态
$daemon = new OrderMonitorDaemon($router);
$daemon->register($entry['router_id'], 'wechat', [
    'out_trade_no' => $order['out_trade_no'],
    'total_fee' => 100,
], [
    'interval' => 5,
    'timeout' => 600,
    'on_success' => function ($paymentData, $routerId) {
        // 通知业务系统、自动发货、推送前端...
    },
]);

$pid = $daemon->runInBackground();

详见 docs/unified_qr.mddocs/personal_receive.md

自动结算插件

<?php

use Kode\Pays\Facade\Pay;
use Kode\Pays\Core\WalletManager;
use Kode\Pays\Plugin\AutoSettlementPlugin;

$walletManager = new WalletManager();

// 绑定微信零钱账户(自动结算)
$walletManager->bind('user_001', 'wechat_wallet', [
    'account' => 'oUpF8uMuAJO_M2pxb1Q9zNjWeS6o',
    'real_name' => '张三',
    'auto_settlement' => true,
    'min_amount' => 100,
    'settlement_type' => 'realtime',
]);

// 绑定银行卡(备用结算方式)
$walletManager->bind('user_001', 'bank_card', [
    'account' => '622202************',
    'real_name' => '张三',
    'bank_code' => 'ICBC',
    'auto_settlement' => true,
    'min_amount' => 5000,
    'settlement_type' => 'daily',
    'settlement_time' => '02:00',
]);

$wechat = Pay::wechat([
    'app_id'  => 'wx123456',
    'mch_id'  => '123456',
    'api_key' => 'your-api-key',
]);

$plugin = new AutoSettlementPlugin($wechat, $walletManager);

// 支付成功后自动触发结算
$order = $wechat->createOrder([
    'out_trade_no' => 'ORDER_' . date('YmdHis'),
    'total_fee'    => 1000,
    'body'         => '商品购买',
]);

$result = $plugin->settle('user_001', [
    'transaction_id' => $order['transaction_id'],
    'amount'         => $order['total_fee'],
    'out_biz_no'     => 'SETTLE_' . date('YmdHis'),
    'description'    => '订单自动结算',
]);

// 批量结算
$results = $plugin->settleBatch([
    ['user_id' => 'user_001', 'transaction_id' => 'T001', 'amount' => 1000, 'out_biz_no' => 'S001'],
    ['user_id' => 'user_002', 'transaction_id' => 'T002', 'amount' => 2000, 'out_biz_no' => 'S002'],
]);

资金操作约束验证

<?php

use Kode\Pays\Core\FundConstraintValidator;
use Kode\Pays\Plugin\TransferPlugin;

$validator = new FundConstraintValidator();

// 配置转账约束
$validator->setTransferConstraints([
    'min_amount' => 100,
    'max_amount' => 200000,
    'daily_limit' => 1000000,
    'daily_count_limit' => 100,
    'allowed_hours' => [9, 22],
    'blacklist' => ['blocked_user_001'],
]);

// 配置红包约束
$validator->setRedPacketConstraints([
    'min_amount' => 100,
    'max_amount' => 200000,
    'max_total_num' => 100,
    'daily_limit' => 500000,
    'daily_count_limit' => 50,
]);

// 创建带约束验证的转账插件
$transferPlugin = new TransferPlugin($wechatGateway, $validator);

// 以下转账将触发约束验证
$result = $transferPlugin->single([
    'out_biz_no'  => 'TRANSFER_001',
    'amount'      => 50000,
    'recipient'   => ['account' => 'openid_xxx', 'name' => '张三'],
    'user_id'     => 'user_001',
]);

异常处理

<?php

use Kode\Pays\Exception\ConfigException;
use Kode\Pays\Exception\NetworkException;
use Kode\Pays\Exception\SignException;
use Kode\Pays\Exception\GatewayException;
use Kode\Pays\Exception\InvalidArgumentException;

try {
    $result = $gateway->createOrder($params);
} catch (ConfigException $e) {
    // 配置缺失或错误,需检查配置文件
} catch (NetworkException $e) {
    // 网络超时或连接失败,可重试
} catch (SignException $e) {
    // 签名验证失败,可能密钥错误或数据被篡改
} catch (InvalidArgumentException $e) {
    // 业务参数错误,需返回给用户
} catch (GatewayException $e) {
    // 网关返回业务错误
    echo '网关错误码:' . ($e->getGatewayCode() ?? '') . PHP_EOL;
    echo '网关错误信息:' . ($e->getGatewayMessage() ?? '') . PHP_EOL;
}

支持的支付网关

网关 标识 支持场景
微信支付 wechat JSAPI、Native、App、H5、小程序、委托代扣(papay)
支付宝 alipay 电脑网站、手机网站、App、小程序、当面付、周期扣款
云闪付 unionpay App、H5、小程序、二维码、个人收款(二维码消费)、代付提现
抖音支付 douyin App、小程序
美团支付 meituan App、外卖、小程序
京东支付 jd App、网页、白条
快手支付 kuaishou App、小程序
QQ 支付 qq QQ 钱包、扫码支付、JSAPI 支付
支付宝国际版 alipay_global 跨境支付、Alipay+
PayPal paypal Checkout、订阅、个人收款(发票二维码、Payouts 提现)
Stripe stripe PaymentIntent、Checkout Session、退款、个人收款、提现(Payouts)
Square square 在线支付、订单管理、Subscriptions、个人收款(Quick Pay)
Adyen adyen 全球 200+ 国家、250+ 支付方式、Recurring 代扣
Amazon Pay amazon 亚马逊账户支付
Klarna klarna 先买后付、分期付款
Apple Pay apple iOS App、网页、手表
Google Pay google Android App、网页
Wise wise 跨境汇款、50+ 货币
Revolut revolut 数字银行支付、卡支付、Apple Pay、Google Pay、个人收款(Merchant Orders)、转账提现
Payoneer payoneer 跨境 B2B 支付、200+ 国家
Coinbase Commerce coinbase 加密货币支付(BTC/ETH/USDT/USDC 等)
Afterpay/Clearpay afterpay 先买后付(BNPL)、4 期免息
HitPay hitpay 新加坡本地支付、东南亚多国家(PayNow/DuitNow/PromptPay/QRIS)
Xendit xendit 东南亚聚合支付(印尼/菲律宾/马来西亚/泰国/越南)
聚合支付 aggregate 多渠道自动路由、失败切换

架构设计

┌─────────────────────────────────────────────────────────────┐
│                      开发者调用层                             │
│         Pay::wechat($config)->createOrder()                  │
├─────────────────────────────────────────────────────────────┤
│                      门面层 (Facade)                          │
│                      Pay 静态类                               │
├─────────────────────────────────────────────────────────────┤
│                      网关工厂层                               │
│                   GatewayFactory::create()                   │
├─────────────────────────────────────────────────────────────┤
│   接口层        │   抽象层         │   具体网关实现            │
│ GatewayInterface │ AbstractGateway │ Wechat/Alipay/Union...  │
├─────────────────────────────────────────────────────────────┤
│   扩展层:事件系统 / 管道中间件 / 配置DTO / 异常子类 / 沙箱管理  │
├─────────────────────────────────────────────────────────────┤
│   支持层:HTTP客户端 / 签名器 / 加密器 / 工具类               │
├─────────────────────────────────────────────────────────────┤
│   插件层:支付 / 退款 / 分账 / 对账 / 转账 / 订阅             │
└─────────────────────────────────────────────────────────────┘

开发规范

本项目遵循 PSR-12 代码规范,使用 declare(strict_types=1) 严格模式。

# 代码检查
composer run phpcs

# 静态分析
composer run phpstan

# 运行测试
composer run test

性能与压测

SDK 提供零网络的微基准套件,用于回归对比热路径吞吐(请求分发、签名/验签、清单反射等):

composer bench          # 等价于 php scripts/bench.php

基准采用 Guzzle MockHandler 模拟零延迟响应,仅测量 SDK 自身开销;实测数据与运行方式见 docs/performance.md。发布前建议运行并与历史数据对比,捕捉显著回退。

生态扩展

Kode Pays SDK 预留了与 kode 系列组件的集成扩展点:

扩展包 功能 集成方式
kode/tools 二维码生成、图片处理 支付码生成
kode/di 依赖注入容器 网关/中间件自动注入
kode/cache 缓存、分布式锁 订单防重、缓存证书
kode/database ORM、分库分表 订单持久化
kode/fibers 协程支持 异步通知并发处理
kode/process 多进程支持 批量任务处理
kode/parallel 多线程支持 并行计算
kode/exception 异常处理增强 异常链追踪、分布式监控上报
kode/facade 门面模式增强 静态代理
kode/limiting 限流保护 支付接口限流(令牌桶/漏桶/滑动窗口)
kode/event 事件总线增强 增强事件分发能力
monolog/monolog PSR-3 日志 支付日志记录

文档导航

完整文档请访问 docs/ 目录:

文档 说明
docs/index.md 文档总览与导航
docs/quickstart.md 快速开始(5 分钟接入)
docs/architecture.md 架构详解与设计模式
docs/development.md 开发指南(新增网关/插件/测试)
docs/plugins.md 插件体系总览
docs/wechat.md 微信支付接入文档
docs/alipay.md 支付宝接入文档
docs/aggregate.md 聚合支付接入文档
docs/personal_receive.md 个人收款验证器(进程内/后台进程抓取)
docs/performance.md 性能与压测数据(热路径基准、运行方式、优化要点)
docs/unified_qr.md 统一收款码路由器(一码聚合多通道 + 后台监控)

各网关接入文档完整列表见 docs/index.md

License

Apache-2.0 License