yansongda/pay
A popular PHP payment library for Laravel and other frameworks, integrating Alipay and WeChat Pay with a clean API. Supports common payment flows, refunds, queries, callbacks/notifications, and flexible configuration for multi-app and sandbox use.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require yansongda/pay:~3.7.0
Target Laravel projects by using yansongda/laravel-pay instead.
Basic Configuration:
Define gateway configurations in a service provider or config file (e.g., config/pay.php):
return [
'alipay' => [
'default' => [
'app_id' => env('ALIPAY_APP_ID'),
'app_secret_cert' => env('ALIPAY_APP_SECRET_CERT'),
'app_public_cert_path' => storage_path('certs/alipay/appCertPublicKey.crt'),
'alipay_public_cert_path' => storage_path('certs/alipay/alipayCertPublicKey_RSA2.crt'),
'alipay_root_cert_path' => storage_path('certs/alipay/alipayRootCert.crt'),
'notify_url' => route('alipay.notify'),
],
],
'logger' => [
'enable' => env('PAY_LOG_ENABLE', false),
'file' => storage_path('logs/pay.log'),
'level' => env('PAY_LOG_LEVEL', 'debug'),
],
];
First Use Case: Trigger a payment in a controller:
use Yansongda\Pay\Pay;
public function pay()
{
Pay::config(config('pay')); // Load configs
$result = Pay::alipay()->web([
'out_trade_no' => 'order_' . time(),
'total_amount' => '10.00',
'subject' => 'Test Payment',
]);
return redirect($result->getRedirectUrl());
}
Callback Handling:
public function notify()
{
Pay::config(config('pay'));
try {
$data = Pay::alipay()->callback(); // Auto-verifies signature
// Process order: $data->out_trade_no, $data->total_amount
return Pay::alipay()->success();
} catch (\Throwable $e) {
return Pay::alipay()->fail();
}
}
// Web payment
$pay = Pay::alipay()->web([
'out_trade_no' => 'order_' . time(),
'total_amount' => '10.00',
'subject' => 'Product Purchase',
'body' => 'Description',
'timeout_express' => '30m', // Optional
]);
// Scan payment (QR code)
$pay = Pay::alipay()->scan([
'out_trade_no' => 'order_' . time(),
'auth_code' => $request->input('auth_code'), // From QR scan
'total_amount' => '10.00',
]);
// Mini Program payment
$pay = Pay::wechat()->mini([
'out_trade_no' => 'order_' . time(),
'description' => 'Mini Program Purchase',
'amount' => ['total' => 1000], // Amount in cents
'payer' => ['openid' => $openid],
]);
// JSAPI (H5) payment
$pay = Pay::wechat()->jsapi([
'out_trade_no' => 'order_' . time(),
'body' => 'H5 Purchase',
'total_fee' => 1000,
]);
// Query order status
$status = Pay::alipay()->query(['out_trade_no' => 'order_123']);
// Refund
$refund = Pay::wechat()->refund([
'out_refund_no' => 'refund_' . time(),
'out_trade_no' => 'order_123',
'refund_amount' => 500, // Partial refund
]);
// Override configs per tenant (e.g., in middleware)
Pay::config([
'alipay' => [
'tenant1' => [
'app_id' => 'tenant1_app_id',
// ...
],
'tenant2' => [
'app_id' => 'tenant2_app_id',
// ...
],
],
]);
// Use tenant-specific configs
Pay::alipay('tenant1')->web([...]);
Listen to payment events (PSR-14) in EventServiceProvider:
protected $listen = [
\Yansongda\Pay\Event\AlipayEvent::ALIPAY_NOTIFY => [
\App\Listeners\HandleAlipayNotify::class,
],
\Yansongda\Pay\Event\WechatEvent::WECHAT_NOTIFY => [
\App\Listeners\HandleWechatNotify::class,
],
];
Extend for unsupported gateways (e.g., PayPal):
// Register in a service provider
Pay::extend('paypal', function () {
return new \Yansongda\Pay\Plugin\PaypalPlugin();
});
// Usage
Pay::paypal()->create([
'intent' => 'sale',
'payer' => ['payment_method' => 'paypal'],
'transactions' => [[
'amount' => ['total' => '10.00'],
'description' => 'Test',
]],
]);
Use queues for callback handling:
// Dispatch callback processing
dispatch(new HandleAlipayNotify($data));
// Listener
public function handle(HandleAlipayNotify $event) {
$data = $event->data;
// Process order update, send notifications, etc.
}
Enable logging in config/pay.php:
'logger' => [
'enable' => true,
'file' => storage_path('logs/pay.log'),
'level' => 'debug', // 'info' for production
],
View logs via Laravel’s log:read command.
For PayPal/Stripe webhooks:
public function handleWebhook()
{
$request = request();
$data = Pay::paypal()->webhook($request); // Auto-verifies signature
// Process webhook event
}
| Issue | Solution |
|---|---|
| Signature verification fails | Ensure notify_url matches exactly (case-sensitive). Check cert paths and permissions. |
| Timeout errors | Increase http.timeout in config (default: 5s). Use async processing for heavy callbacks. |
| Multi-tenant conflicts | Explicitly specify tenant: Pay::alipay('tenant1')->web([...]). |
| WeChat cert errors | Use wechat_public_cert_path in config. For PHP-FPM, set wechat_public_cert_path to an array of certs. |
| Refund failures | Verify out_refund_no is unique and out_trade_no exists. |
Pay::config(['logger' => ['level' => 'debug']]);
$raw = Pay::alipay()->getLastRequest(); // For Alipay
$raw = Pay::wechat()->getLastRequest(); // For WeChat
'mode' => Pay::MODE_SANDBOX, // For Alipay/WeChat
storage/app/certs/ and set proper permissions (chmod 644).'wechat_public_cert_path' => [
'CERT_ID_1' => 'path/to/cert1.pem',
'CERT_ID_2' => 'path/to/cert2.pem',
],
10.00).1000 for ¥10.00).Override default behavior by creating a plugin:
namespace App\Plugins;
use Yansongda\
How can I help you explore Laravel packages today?