Search by

kode / pays

kodephp

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

Package info

github.com/kodephp/pays

pkg:composer/kode/pays

Statistics

Installs: 258

Dependents: 1

Suggesters: 0

Stars: 1

Open Issues: 1

2.32.0 2026-09-22 09:17 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-entry.mddocs/personal-receive-verifier.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)
微信小程序虚拟支付 wechat_virtual 虚拟商品道具直购(short_series_goods)、代币充值/扣币/退款/赠送、对账、发货回执
支付宝 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/wechat-virtual.md 微信小程序虚拟支付(xpay)接入文档
docs/alipay.md 支付宝接入文档
docs/aggregate.md 聚合支付接入文档
docs/personal-receive-verifier.md 个人收款验证器(进程内/后台进程抓取)
docs/performance.md 性能与压测数据(热路径基准、运行方式、优化要点)
docs/unified-qr-entry.md 统一收款码路由器(一码聚合多通道 + 后台监控)

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

License

Apache-2.0 License