@@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
namespace App\Billing\Gateways;
|
||||
|
||||
use App\Billing\Contracts\PaymentGateway;
|
||||
use App\Models\BillingOrder;
|
||||
use Illuminate\Http\Client\PendingRequest;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
class DuitkuPaymentGateway implements PaymentGateway
|
||||
{
|
||||
public function paymentMethods(BillingOrder $order): array
|
||||
{
|
||||
[$merchant, $key] = $this->credentials();
|
||||
$amount = (int) $order->total;
|
||||
$dateTime = now('Asia/Jakarta')->format('Y-m-d H:i:s');
|
||||
$response = $this->http()->post(
|
||||
$this->baseUrl().'/webapi/api/merchant/paymentmethod/getpaymentmethod',
|
||||
[
|
||||
'merchantcode' => $merchant,
|
||||
'amount' => $amount,
|
||||
'datetime' => $dateTime,
|
||||
'signature' => hash_hmac('sha256', $merchant.$amount.$dateTime, $key),
|
||||
]
|
||||
);
|
||||
|
||||
if (! $response->successful() || ! is_array($response->json('paymentFee'))) {
|
||||
throw new RuntimeException('PAYMENT_METHODS_REJECTED: '.($response->json('responseMessage') ?: 'Daftar kanal pembayaran Duitku tidak tersedia.'));
|
||||
}
|
||||
|
||||
return collect($response->json('paymentFee'))->map(fn (array $method): array => [
|
||||
'code' => (string) $method['paymentMethod'],
|
||||
'name' => (string) $method['paymentName'],
|
||||
'fee' => (int) ($method['totalFee'] ?? 0),
|
||||
])->values()->all();
|
||||
}
|
||||
|
||||
public function createCheckout(BillingOrder $order): array
|
||||
{
|
||||
[$merchant, $key] = $this->credentials();
|
||||
if (! $order->payment_method) {
|
||||
throw new RuntimeException('PAYMENT_METHOD_REQUIRED: Pilih kanal pembayaran terlebih dahulu.');
|
||||
}
|
||||
$externalId = 'RNDM-'.$order->uuid;
|
||||
$user = $order->tenant->users()->where('is_active', true)->oldest()->firstOrFail();
|
||||
$amount = (int) $order->total;
|
||||
$payload = [
|
||||
'merchantCode' => $merchant, 'paymentAmount' => $amount, 'merchantOrderId' => $externalId,
|
||||
'paymentMethod' => $order->payment_method,
|
||||
'productDetails' => 'Lisensi bulanan RADIQ NDM '.config("billing.plans.{$order->plan_code}.name"),
|
||||
'email' => $user->email, 'customerVaName' => $user->name,
|
||||
'callbackUrl' => route('payments.webhook', ['gateway' => 'duitku']),
|
||||
'returnUrl' => route('checkout.return', $order->uuid), 'expiryPeriod' => 1440,
|
||||
'signature' => hash_hmac('sha256', $merchant.$externalId.$amount, $key),
|
||||
];
|
||||
$response = $this->http()->post($this->baseUrl().'/webapi/api/merchant/v2/inquiry', $payload);
|
||||
if (! $response->successful() || ! $response->json('paymentUrl')) {
|
||||
throw new RuntimeException('PAYMENT_GATEWAY_REJECTED: '.($response->json('Message') ?: $response->json('message') ?: 'Duitku tidak menghasilkan URL pembayaran.'));
|
||||
}
|
||||
|
||||
return ['external_id' => $externalId, 'checkout_url' => $response->json('paymentUrl'), 'metadata' => ['reference' => $response->json('reference')]];
|
||||
}
|
||||
|
||||
public function verifyWebhook(array $payload, array $headers = []): bool
|
||||
{
|
||||
$merchant = (string) ($payload['merchantCode'] ?? '');
|
||||
$amount = (string) ($payload['amount'] ?? '');
|
||||
$orderId = (string) ($payload['merchantOrderId'] ?? '');
|
||||
$signature = (string) ($payload['signature'] ?? '');
|
||||
$expected = hash_hmac('sha256', $merchant.$amount.$orderId, (string) config('billing.duitku.api_key'));
|
||||
|
||||
return $merchant !== '' && hash_equals((string) config('billing.duitku.merchant_code'), $merchant) && hash_equals($expected, $signature);
|
||||
}
|
||||
|
||||
public function statusFromWebhook(array $payload): string
|
||||
{
|
||||
return ($payload['resultCode'] ?? null) === '00' ? 'paid' : 'failed';
|
||||
}
|
||||
|
||||
public function externalIdFromWebhook(array $payload): ?string
|
||||
{
|
||||
return isset($payload['merchantOrderId']) ? (string) $payload['merchantOrderId'] : null;
|
||||
}
|
||||
|
||||
/** @return array{string,string} */
|
||||
private function credentials(): array
|
||||
{
|
||||
$merchant = (string) config('billing.duitku.merchant_code');
|
||||
$key = (string) config('billing.duitku.api_key');
|
||||
if ($merchant === '' || $key === '') {
|
||||
throw new RuntimeException('PAYMENT_GATEWAY_NOT_CONFIGURED: Merchant Code atau API Key Duitku belum diisi.');
|
||||
}
|
||||
|
||||
return [$merchant, $key];
|
||||
}
|
||||
|
||||
private function baseUrl(): string
|
||||
{
|
||||
return config('billing.duitku.sandbox') ? 'https://sandbox.duitku.com' : 'https://passport.duitku.com';
|
||||
}
|
||||
|
||||
private function http(): PendingRequest
|
||||
{
|
||||
$request = Http::asJson()->acceptJson()->timeout(20);
|
||||
$caBundle = config('billing.duitku.ca_bundle');
|
||||
|
||||
return is_string($caBundle) && $caBundle !== ''
|
||||
? $request->withOptions(['verify' => $caBundle])
|
||||
: $request;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace App\Billing\Gateways;
|
||||
|
||||
use App\Billing\Contracts\PaymentGateway;
|
||||
use App\Models\BillingOrder;
|
||||
|
||||
class ManualPaymentGateway implements PaymentGateway
|
||||
{
|
||||
public function paymentMethods(BillingOrder $order): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function createCheckout(BillingOrder $order): array
|
||||
{
|
||||
return ['external_id' => 'MANUAL-'.$order->uuid, 'checkout_url' => null, 'metadata' => ['instruction' => 'Hubungi RADIQ untuk instruksi pembayaran. Aktivasi dilakukan setelah pembayaran diverifikasi.']];
|
||||
}
|
||||
|
||||
public function verifyWebhook(array $payload, array $headers = []): bool
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function statusFromWebhook(array $payload): string
|
||||
{
|
||||
return 'pending';
|
||||
}
|
||||
|
||||
public function externalIdFromWebhook(array $payload): ?string
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user