laravel-sdk maintained by winpay
Winpay Laravel SDK
Library Laravel resmi untuk Winpay Payment Gateway — integrasi SNAP API & Checkout Page.
Requires: PHP ^8.1, Laravel ^9.0 | ^10.0 | ^11.0
Install
composer require winpay/laravel-sdk
Service provider & facade terdaftar otomatis via auto-discovery.
Konfigurasi
Environment Variables (.env)
WINPAY_PARTNER_ID=your_partner_id
WINPAY_MERCHANT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQ..."
WINPAY_PUBLIC_KEY="-----BEGIN PUBLIC KEY-----\nMIIBIj..."
WINPAY_CHANNEL_ID=WEB
WINPAY_BASE_URL=https://sandbox-api.bmstaging.id/snap
WINPAY_TIMEOUT=30
WINPAY_VERIFY_SSL=true
WINPAY_REGISTER_ROUTES=true
# Checkout Page (HMAC)
WINPAY_CHECKOUT_KEY=your_checkout_key
WINPAY_CHECKOUT_SECRET_KEY=your_checkout_secret_key
WINPAY_CHECKOUT_BASE_URL=https://sandbox-checkout.winpay.id
Key bisa inline PEM string atau path file:
# Inline
WINPAY_MERCHANT_PRIVATE_KEY="-----BEGIN PRIVATE KEY-----\nMIIEvQ..."
# File path
WINPAY_MERCHANT_PRIVATE_KEY=/path/to/merchant-private.pem
Publish Config (opsional)
php artisan vendor:publish --tag=winpay-config
Cara Pakai
Via Facade
use Winpay;
$response = Winpay::snap()->va()->create([...]);
Via Dependency Injection
use Winpay\Core\WinpayClient;
class OrderController
{
public function __construct(
private readonly WinpayClient $winpay,
) {}
public function store()
{
$response = $this->winpay->va->create([...]);
}
}
Response Format
Semua method mengembalikan Winpay\Core\Models\WinpayResponse:
$response->responseCode // string|null, e.g. "2002700"
$response->responseMessage // string|null, e.g. "Successful"
$response->httpStatusCode // int, e.g. 200
$response->data // array, full response payload
$response->isSuccess() // bool, true jika 2xx
$response->get('key') // mixed, akses data by key
$response->get('key', 'default')
Services
Gunakan Winpay::snap() untuk akses semua channel dengan verb konsisten:
$snap = Winpay::snap();
$snap->va()->create($payload)
$snap->va()->inquiry($payload)
$snap->va()->status($payload)
$snap->va()->delete($payload)
$snap->qris()->create($payload)
$snap->qris()->status($payload)
$snap->qris()->cancel($payload)
$snap->ewallet()->create($payload)
$snap->ewallet()->status($payload)
$snap->ewallet()->cancel($payload)
$snap->creditCard()->create($payload)
$snap->creditCard()->status($payload)
$snap->creditCard()->cancel($payload)
$snap->retail()->create($payload)
$snap->retail()->status($payload)
$snap->retail()->cancel($payload)
$snap->report()->balance($payload)
$snap->report()->history($payload)
$snap->report()->statement($payload)
# Checkout Page (HMAC)
$snap->checkout()->create($payload)
$snap->checkout()->find($id)
$snap->checkout()->findByRef($merchantRef)
$snap->checkout()->update($id, $payload)
$snap->checkout()->delete($id)
$snap->checkout()->deleteByRef($merchantRef)
Virtual Account
use Winpay\Core\Enums\VaChannel;
use Winpay\Core\Enums\VaType;
// Create VA
$va = Winpay::snap()->va()->create([
'partnerServiceId' => '35539457',
'customerNo' => '9876543210',
'virtualAccountNo' => '355394579876543210',
'virtualAccountName' => 'John Doe',
'virtualAccountTrxType' => VaType::ONE_OFF->value,
'totalAmount' => ['value' => '100000.00', 'currency' => 'IDR'],
'expiredDate' => '2026-12-31T23:59:59+07:00',
]);
// Inquiry VA
$va = Winpay::snap()->va()->inquiry([
'partnerServiceId' => '35539457',
'customerNo' => '9876543210',
'virtualAccountNo' => '355394579876543210',
]);
// Status VA
$va = Winpay::snap()->va()->status([
'partnerServiceId' => '35539457',
'customerNo' => '9876543210',
'virtualAccountNo' => '355394579876543210',
]);
// Delete VA
Winpay::snap()->va()->delete([
'partnerServiceId' => '35539457',
'customerNo' => '9876543210',
'virtualAccountNo' => '355394579876543210',
]);
Bank Channel (VaChannel)
BRI, BNI, MANDIRI, MANDIRI_PC, PERMATA, BSI, MUAMALAT, BCA, CIMB, SINARMAS, BNC, MAYBANK
VA Type (VaType)
| Enum | Value | Keterangan |
|---|---|---|
VaType::ONE_OFF |
c |
Dibayar sekali, closed setelah dibayar |
VaType::OPEN_RECURRING |
o |
Bisa dipakai berulang, nominal bebas |
VaType::CLOSE_RECURRING |
r |
Bisa dipakai berulang, nominal tetap |
QRIS
// Generate QRIS
$qris = Winpay::snap()->qris()->create([
'partnerServiceId' => '35539457',
'amount' => [
'value' => '50000.00',
'currency' => 'IDR',
],
'merchantId' => 'MERCHANT001',
'storeId' => 'STORE001',
'validityPeriod' => '2026-12-31T23:59:59+07:00',
]);
// Query QRIS
$qris = Winpay::snap()->qris()->status([
'originalReferenceNo' => 'ORIG_REF_001',
]);
// Cancel QRIS
Winpay::snap()->qris()->cancel([
'originalReferenceNo' => 'ORIG_REF_001',
]);
E-Wallet
use Winpay\Core\Enums\EwalletChannel;
// Create Payment
$ewallet = Winpay::snap()->ewallet()->create([
'partnerReferenceNo' => 'REF-001',
'amount' => ['value' => '25000.00', 'currency' => 'IDR'],
'additionalInfo' => [
'channel' => EwalletChannel::DANA->value,
'customerName' => 'John Doe',
],
]);
// Status
$ewallet = Winpay::snap()->ewallet()->status([
'originalReferenceNo' => 'REF-001',
]);
// Cancel
Winpay::snap()->ewallet()->cancel([
'originalReferenceNo' => 'REF-001',
]);
Channel (EwalletChannel)
| Enum | Value |
|---|---|
EwalletChannel::SPEEDCASH |
SC |
EwalletChannel::OVO |
OVO |
EwalletChannel::DANA |
DANA |
EwalletChannel::SHOPEEPAY |
SPAY |
EwalletChannel::ASTRAPAY |
ASTRA |
Credit Card
// Create Payment
$cc = Winpay::snap()->creditCard()->create([
'partnerReferenceNo' => 'REF-001',
'amount' => ['value' => '500000.00', 'currency' => 'IDR'],
'additionalInfo' => [
'channel' => 'CC',
'customerName' => 'John Doe',
],
]);
// Status
$cc = Winpay::snap()->creditCard()->status([...]);
// Cancel
Winpay::snap()->creditCard()->cancel([...]);
Retail / OTC
use Winpay\Core\Enums\RetailChannel;
$retail = Winpay::snap()->retail()->create([
'partnerServiceId' => '35539457',
'customerNo' => '9876543210',
'paymentCode' => '355394579876543210',
'customerName' => 'John Doe',
'totalAmount' => ['value' => '50000.00', 'currency' => 'IDR'],
'additionalInfo' => [
'channel' => RetailChannel::ALFAMART->value,
],
]);
// Status
$retail = Winpay::snap()->retail()->status([
'originalReferenceNo' => 'REF-001',
]);
// Cancel
Winpay::snap()->retail()->cancel([
'originalReferenceNo' => 'REF-001',
]);
Channel (RetailChannel)
| Enum | Value |
|---|---|
RetailChannel::ALFAMART |
ALFAMART |
RetailChannel::INDOMARET |
INDOMARET |
RetailChannel::FASTPAY |
FASTPAY |
Report
// Balance Inquiry
$balance = Winpay::snap()->report()->balance([
'partnerServiceId' => '35539457',
]);
// Transaction History
$history = Winpay::snap()->report()->history([
'fromDateTime' => '2026-01-01T00:00:00+07:00',
'toDateTime' => '2026-12-31T23:59:59+07:00',
]);
// Bank Statement
$statement = Winpay::snap()->report()->statement([
'accountNo' => '1234567890',
'fromDateTime' => '2026-01-01T00:00:00+07:00',
'toDateTime' => '2026-12-31T23:59:59+07:00',
]);
Checkout Page
Gunakan HMAC-SHA256 signature dengan header X-Winpay-Key, X-Winpay-Timestamp, X-Winpay-Signature.
// Create Invoice
$invoice = Winpay::snap()->checkout()->create([
'customer' => [
'name' => 'John Doe',
'email' => 'john@example.com',
'phone' => '081234567890',
],
'items' => [
[
'id' => 'ITEM001',
'name' => 'Product A',
'price' => 25000,
'quantity' => 2,
],
],
'totalAmount' => 50000,
'merchantRef' => 'INV-001',
'expiryMinutes' => 60,
'successUrl' => 'https://example.com/success',
'failureUrl' => 'https://example.com/failure',
]);
// Find by ID
$invoice = Winpay::snap()->checkout()->find('inv-id-123');
// Find by Merchant Reference
$invoice = Winpay::snap()->checkout()->findByRef('INV-001');
// Update Invoice
$invoice = Winpay::snap()->checkout()->update('inv-id-123', [
'totalAmount' => 75000,
]);
// Delete by ID
Winpay::snap()->checkout()->delete('inv-id-123');
// Delete by Merchant Reference
Winpay::snap()->checkout()->deleteByRef('INV-001');
Callback / Webhook
Routes terdaftar otomatis saat provider di-boot. Bisa disable dengan:
WINPAY_REGISTER_ROUTES=false
Endpoint
| Method | URL | Handler | Channel |
|---|---|---|---|
| POST | /api/winpay/callback/v1.0/transfer-va/payment |
vaPayment() |
VA |
| POST | /api/winpay/callback/v1.0/qr/qr-mpm-notify |
qrisNotify() |
QRIS |
| POST | /api/winpay/callback/v1.0/debit/notify |
debitNotify() |
eWallet / CC |
Setiap callback akan:
- Validasi tanda tangan RSA-SHA256 via
WinpayClient::verifyCallback() - Dispatch event
WinpayCallbackReceived - Return
responseCode+responseMessagesesuai standar SNAP (401 jika signature invalid)
Custom Prefix
// Di routes/api.php
Winpay\Laravel\WinpayServiceProvider::callbackRoutes('webhook/winpay');
Listening Event
// Di App\Providers\EventServiceProvider.php
use Winpay\Laravel\Events\WinpayCallbackReceived;
protected $listen = [
WinpayCallbackReceived::class => [
HandleWinpayCallback::class,
],
];
class HandleWinpayCallback
{
public function handle(WinpayCallbackReceived $event): void
{
if (! $event->verified) {
logger()->warning('Winpay callback invalid signature', [
'type' => $event->type,
]);
return;
}
if ($event->transactionStatus === '00') {
// Process successful payment
// $event->type: 'va' | 'qris' | 'ewallet' | 'credit_card'
// $event->payload: full request body
}
}
}
Error Handling
use Winpay\Core\Exceptions\WinpayException;
use Winpay\Core\Exceptions\HttpException;
try {
$result = Winpay::snap()->va()->create([...]);
} catch (HttpException $e) {
// 4xx/5xx — error dari server Winpay
$e->getStatusCode(); // int
$e->getMessage(); // string
$e->getErrors(); // array|null (responseCode, responseMessage, full payload)
} catch (WinpayException $e) {
// Config error, signature error, network error
}
Transaction Status (TransactionStatus)
| Enum | Value | Arti |
|---|---|---|
TransactionStatus::SUCCESS |
00 |
Sukses |
TransactionStatus::INITIATED |
01 |
Initiated |
TransactionStatus::PAYING |
02 |
Sedang dibayar |
TransactionStatus::PENDING |
03 |
Pending |
TransactionStatus::REFUNDED |
04 |
Refund |
TransactionStatus::CANCELED |
05 |
Dibatalkan |
TransactionStatus::FAILED |
06 |
Gagal |
TransactionStatus::NOT_FOUND |
07 |
Tidak ditemukan |
Testing
php vendor/bin/phpunit