laraditz/xendit 问题修复 & 功能扩展

解决BUG、新增功能、兼容多环境部署,快速响应你的开发需求

邮箱:yvsm@zunyunkeji.com | QQ:316430983 | 微信:yvsm316

laraditz/xendit

Composer 安装命令:

composer require laraditz/xendit

包简介

Laravel package for interacting with Xendit API.

README 文档

README

Latest Version on Packagist Total Downloads GitHub Actions

A Laravel package for seamless integration with Xendit payment gateway. Built from scratch using Laravel's HTTP client, this package provides a fluent API for creating payments, managing transactions, and handling webhooks - all with database persistence and event-driven architecture.

Features

  • 🔥 Fluent API for creating payments
  • 💳 Support for all payment methods (E-Wallet, Virtual Account, QR Code, Cards, OTC)
  • 🗄️ Database persistence for payments, transactions, and webhooks
  • 🔔 Automatic webhook handling with signature verification
  • 🎯 Event-driven architecture (Payment, Refund, Token events)
  • 🔗 Polymorphic relationships (attach payments to any model)
  • 📦 Uses Laravel's HTTP client (no Guzzle dependency)
  • 🛡️ Type-safe with PHP 8.2+ Backed Enums
  • 💰 Complete refund management
  • 🔑 Payment token (saved payment methods) support
  • 🔗 Payment links generation
  • 📊 Transaction querying and listing
  • 🎫 Session management
  • 🏷️ Custom HTTP headers on any API call (for-user-id, with-split-rule, etc.)

API Coverage

Requirements

  • PHP 8.2+
  • Laravel 10.x, 11.x, or 12.x

Installation

Install the package via composer:

composer require laraditz/xendit

Publish the configuration and migrations:

php artisan vendor:publish --tag=xendit-config
php artisan vendor:publish --tag=xendit-migrations

Run the migrations:

php artisan migrate

Run the seeder:

php artisan db:seed --class=Laraditz\\Xendit\\Database\\Seeders\\DatabaseSeeder

Add your Xendit credentials to .env:

XENDIT_API_KEY=your-secret-api-key
XENDIT_WEBHOOK_SECRET=your-webhook-verification-token
XENDIT_CURRENCY=MYR

Usage

Creating Payment Request (Fluent API)

use Laraditz\Xendit\Facades\Xendit;

$payment = Xendit::paymentRequest()
    ->amount(100000)
    ->currency('MYR')
    ->description('Payment for Order #123')
    ->ewallets('SHOPEEPAY') // Specify channel code
    ->successUrl('https://yourapp.com/success')
    ->failureUrl('https://yourapp.com/failed')
    ->metadata(['order_id' => 123])
    ->create();

// Redirect user to payment page
return redirect($payment->payment_url);

Payment Request with Array

use Laraditz\Xendit\Facades\Xendit;

// Using array parameter
$payment = Xendit::paymentRequest()->create([
    'reference_id' => 'ORDER-123',
    'amount' => 100000,
    'currency' => 'MYR',
    'channel_code' => 'SHOPEEPAY',
    'channel_properties' => [
        'success_return_url' => 'https://yourapp.com/success',
        'failure_return_url' => 'https://yourapp.com/failed',
    ],
    'description' => 'Payment for Order #123',
    'metadata' => [
        'order_id' => 123,
    ],
]);

Payment Request with Specific Channels

use Laraditz\Xendit\Facades\Xendit;

// E-wallet (ShopeePay)
$payment = Xendit::paymentRequest()
    ->amount(50000)
    ->ewallets('SHOPEEPAY')
    ->successUrl('https://yourapp.com/success')
    ->create();

// Virtual Account (BCA)
$payment = Xendit::paymentRequest()
    ->amount(50000)
    ->virtualAccounts('BCA')
    ->create();

// QR Code (QRIS)
$payment = Xendit::paymentRequest()
    ->amount(75000)
    ->qrCode('QRIS')
    ->create();

// Cards
$payment = Xendit::paymentRequest()
    ->amount(100000)
    ->card()
    ->create();

// Or use specific channel code directly
$payment = Xendit::paymentRequest()
    ->amount(100000)
    ->channelCode('GRABPAY')
    ->create();

Payment Request with Channel Properties

use Laraditz\Xendit\Facades\Xendit;

// Using channel properties for additional configuration
$payment = Xendit::paymentRequest()
    ->amount(250000)
    ->currency('MYR')
    ->channelCode('SHOPEEPAY')
    ->channelProperties('SHOPEEPAY', [
        'success_return_url' => 'https://yourapp.com/success',
        'failure_return_url' => 'https://yourapp.com/failed',
    ])
    ->metadata([
        'order_id' => 123,
    ])
    ->create();

Attaching Payments to Models (Polymorphic)

// Attach payment to Order model
$payment = Xendit::paymentRequest()
    ->amount(100000)
    ->currency('MYR')
    ->ewallets('SHOPEEPAY')
    ->for($order)
    ->create();

// Attach payment to User model
$payment = Xendit::paymentRequest()
    ->amount(50000)
    ->card()
    ->for($user)
    ->create();

// Access payments from your model
$order->payments; // Get all payments for this order

Managing Payments

use Laraditz\Xendit\Facades\Xendit;

// Get payment status
$status = Xendit::payment()->get($paymentId);

// Cancel a payment
Xendit::payment()->cancel($paymentId);

// Capture a payment (for authorized payments)
Xendit::payment()->capture($paymentId, [
    'capture_amount' => 100000,
]);

Working with Payment Tokens (Saved Payment Methods)

use Laraditz\Xendit\Facades\Xendit;

// Create a payment token
$token = Xendit::paymentToken()->create([
    'customer_id' => 'customer-123',
    'type' => 'CARD',
    // ... other token data
]);

// Get token status
$tokenStatus = Xendit::paymentToken()->get($tokenId);

// Deactivate a token
Xendit::paymentToken()->cancel($tokenId);

Creating Customers

use Laraditz\Xendit\Facades\Xendit;

// Create an individual customer
$customer = Xendit::customer()
    ->referenceId('user-001')
    ->givenNames('John')
    ->surname('Doe')
    ->email('john@example.com')
    ->mobileNumber('+60123456789')
    ->create();

// $customer is a persisted XenditCustomer model
echo $customer->xendit_id; // Xendit's customer ID

// Get a customer by Xendit ID
$data = Xendit::customer()->get('cust_abc123');

// List customers by reference ID
$list = Xendit::customer()->list('user-001');

// Update a customer
$updated = Xendit::customer()->update('cust_abc123', [
    'email' => 'new@example.com',
]);

Creating Sessions

use Laraditz\Xendit\Facades\Xendit;

// Create a PAY session (Payment Link mode)
$session = Xendit::session()
    ->referenceId('order-001')
    ->amount(100.00)
    ->currency('MYR')
    ->country('MY')
    ->sessionType('PAY')
    ->mode('PAYMENT_LINK')
    ->successUrl('https://yourapp.com/success')
    ->cancelUrl('https://yourapp.com/cancel')
    ->create();

// $session is a persisted XenditSession model
return redirect($session->payment_link_url);

// Get session details from Xendit API
$data = Xendit::session()->get($session->payment_session_id);

// Cancel a session
Xendit::session()->cancel($session->payment_session_id);

Processing Refunds

use Laraditz\Xendit\Facades\Xendit;

// Create a refund
$refund = Xendit::refund()->create([
    'payment_id' => $paymentId,
    'amount' => 50000,
    'reason' => 'Customer request',
]);

Creating Payment Links

use Laraditz\Xendit\Facades\Xendit;

// Create a payment link
$link = Xendit::paymentLink()->create([
    'amount' => 100000,
    'description' => 'Payment for Product',
    'customer' => [
        'email' => 'customer@example.com',
    ],
]);

// Get payment link
$linkDetails = Xendit::paymentLink()->get($linkId);

Querying Transactions

use Laraditz\Xendit\Facades\Xendit;

// Get transaction by ID
$transaction = Xendit::transaction()->get($transactionId);

// List all transactions
$transactions = Xendit::transaction()->list([
    'limit' => 20,
    'after_id' => 'txn_123',
]);

Per-Service API Versions

By default no api-version header is sent. You can configure per-service defaults in config/xendit.php, override them per call, or suppress them entirely:

// config/xendit.php — configure per-service defaults (all optional)
'api_versions' => [
    'payment_request' => '2024-11-11',
    'session'         => '2024-05-01',
    // any service key set to null suppresses the header even if the service has a default
    // 'payment' => null,
],
// Per-call override — takes precedence over config
Xendit::session()
    ->withApiVersion('2024-05-01')
    ->amount(100.00)
    ->sessionType('PAY')
    ->mode('PAYMENT_LINK')
    ->create();

// Suppress for this call only
Xendit::session()
    ->withoutApiVersion()
    ->get('ps-abc123');

Available config keys: payment_request, payment, payment_token, session, customer, refund, payment_link, transaction.

Custom HTTP Headers (Sub-accounts & Split Rules)

Any builder supports arbitrary request headers via withHeader() / withHeaders():

// Single header
Xendit::session()->withHeader('idempotency-key', 'abc')->create();

// Multiple headers at once
Xendit::session()->withHeaders(['idempotency-key' => 'abc'])->create();

// Works on get() and cancel() too
Xendit::session()->withHeader('for-user-id', 'sub-account-id')->get('ps-abc123');

SessionBuilder and PaymentRequestBuilder also expose named shortcuts that set the header and persist the value to the database for filtering and auditing:

// Session — for-user-id header + xendit_sessions.for_user_id column
$session = Xendit::session()
    ->forUserId('sub-account-user-id')   // sets 'for-user-id' header
    ->withSplitRule('split-rule-id')      // sets 'with-split-rule' header
    ->amount(100.00)
    ->sessionType('PAY')
    ->mode('PAYMENT_LINK')
    ->create();

// Payment Request — same pattern → xendit_payments.for_user_id / split_rule_id
$payment = Xendit::paymentRequest()
    ->forUserId('sub-account-user-id')
    ->withSplitRule('split-rule-id')
    ->amount(100.00)
    ->ewallets('SHOPEEPAY')
    ->create();

// Query by sub-account or split rule
XenditSession::forUserId('sub-account-user-id')->get();
XenditPayment::forUserId('sub-account-user-id')->get();
XenditSession::splitRuleId('split-rule-id')->get();
XenditPayment::splitRuleId('split-rule-id')->get();

Querying Payments

use Laraditz\Xendit\Models\XenditPayment;
use Laraditz\Xendit\Enums\PaymentStatus;

// Find payment by external ID
$payment = XenditPayment::externalId('ORDER-123')->first();

// Find paid payments
$paidPayments = XenditPayment::paid()->get();

// Find pending payments
$pendingPayments = XenditPayment::pending()->get();

// Filter by status
$payments = XenditPayment::status(PaymentStatus::Paid)->get();

// Check payment status
if ($payment->isPaid()) {
    // Payment is paid
}

Webhook Handling

Webhooks are automatically handled at /xendit/webhook. The package will:

  1. Verify webhook signature
  2. Log webhook to database
  3. Update payment status
  4. Dispatch Laravel events

Available Webhook Events

Listen to webhook events in your EventServiceProvider:

use Laraditz\Xendit\Events\PaymentPaid;
use Laraditz\Xendit\Events\PaymentExpired;
use Laraditz\Xendit\Events\PaymentFailed;
use Laraditz\Xendit\Events\PaymentTokenCreated;
use Laraditz\Xendit\Events\PaymentTokenActivated;
use Laraditz\Xendit\Events\RefundCreated;
use Laraditz\Xendit\Events\RefundSucceeded;
use Laraditz\Xendit\Events\SessionCreated;
use Laraditz\Xendit\Events\SessionCompleted;
use Laraditz\Xendit\Events\SessionExpired;
use Laraditz\Xendit\Events\SessionCanceled;

protected $listen = [
    // Payment events
    PaymentPaid::class => [
        SendPaymentConfirmationEmail::class,
        ProcessOrder::class,
    ],
    PaymentExpired::class => [
        CancelOrder::class,
    ],
    PaymentFailed::class => [
        NotifyPaymentFailure::class,
    ],

    // Payment token events
    PaymentTokenCreated::class => [
        LogPaymentTokenCreation::class,
    ],
    PaymentTokenActivated::class => [
        EnableSavedPaymentMethod::class,
    ],

    // Refund events
    RefundCreated::class => [
        LogRefundRequest::class,
    ],
    RefundSucceeded::class => [
        ProcessRefund::class,
    ],

    // Session events
    SessionCreated::class => [
        LogSessionCreated::class,
    ],
    SessionCompleted::class => [
        FulfillOrder::class,
    ],
    SessionExpired::class => [
        CancelOrder::class,
    ],
    SessionCanceled::class => [
        ReleaseReservedStock::class,
    ],
];

Example Listeners

Payment Event Listener:

namespace App\Listeners;

use Laraditz\Xendit\Events\PaymentPaid;

class ProcessOrder
{
    public function handle(PaymentPaid $event)
    {
        $payment = $event->payment;

        // Access related model
        $order = $payment->payable; // Returns Order model

        // Process the order
        $order->markAsPaid();
        $order->process();
    }
}

Refund Event Listener:

namespace App\Listeners;

use Laraditz\Xendit\Events\RefundSucceeded;

class ProcessRefund
{
    public function handle(RefundSucceeded $event)
    {
        $refundData = $event->payload;

        // Process refund
        $order = Order::where('payment_id', $refundData['payment_id'])->first();
        $order->markAsRefunded();
    }
}

Payment Token Event Listener:

namespace App\Listeners;

use Laraditz\Xendit\Events\PaymentTokenCreated;

class LogPaymentTokenCreation
{
    public function handle(PaymentTokenCreated $event)
    {
        $tokenData = $event->payload;

        // Store token reference or log
        Log::info('Payment token created', $tokenData);
    }
}

Documentation

For detailed documentation on each service, please refer to:

  • Payment Request - Complete guide to creating payment requests with fluent builder
  • Payment - Managing payment status, cancellation, and capture
  • Payment Token - Saving and managing customer payment methods
  • Customer - Creating and managing Xendit customers with DB persistence
  • Session - Creating secure payment sessions for checkout flows
  • Refund - Processing full and partial refunds
  • Payment Link - Generating shareable payment links for invoices
  • Transaction - Querying and listing all transactions
  • Webhooks - Handling webhook events and notifications

Testing

composer test

Changelog

Please see CHANGELOG for more information what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email raditzfarhan@gmail.com instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

laraditz/xendit 适用场景与选型建议

laraditz/xendit 是一款 基于 PHP 开发的 Composer 扩展包,目前已累计 70 次下载、GitHub Stars 达 0, 最近一次更新时间为 2026 年 01 月 04 日, 在 PHP 生态内属于活跃度较高的组件。

它主要适用于以下技术方向: 「xendit」 「laraditz」 等业务场景。在实际项目中,围绕这些方向常见需要落地的问题包括:接口对接、性能调优、并发安全、与既有框架(Laravel / ThinkPHP / Yii / Webman 等)的兼容适配,以及生产环境的日志埋点与稳定性保障。

我们在过去多个企业项目中使用过 laraditz/xendit 或与其功能相近的方案,如果你在选型或落地过程中遇到问题,例如 版本兼容、二次改造、私有化封装、与内部系统对接、生产 BUG 排查,欢迎联系我们协助评估。

围绕 laraditz/xendit 我们能提供哪些服务?
定制开发 / 二次开发

基于 laraditz/xendit 在你已有业务上做功能扩展、字段裁剪、UI 适配、与内部账号 / 权限 / 日志系统的深度对接。

BUG 修复 & 性能优化

线上偶发问题、内存泄漏、慢查询、并发异常等排查修复;针对高流量场景做缓存、队列、索引层面的调优。

项目外包 & 长期维护

承接完整的项目从需求 → 设计 → 开发 → 上线 → 长期运维;也可按月提供技术保姆服务。

yvsm@zunyunkeji.com QQ:316430983 微信:yvsm316 西安尊云信息科技 · 专注 PHP / Go / 分布式系统研发

统计信息

  • 总下载量: 70
  • 月度下载量: 0
  • 日度下载量: 0
  • 收藏数: 0
  • 点击次数: 24
  • 依赖项目数: 0
  • 推荐数: 0

GitHub 信息

  • Stars: 0
  • Watchers: 0
  • Forks: 1
  • 开发语言: PHP

其他信息

  • 授权协议: MIT
  • 更新时间: 2026-01-04