Created
June 9, 2025 03:29
-
-
Save kura1420/f0634265f4c111dff55455625fa88763 to your computer and use it in GitHub Desktop.
Laravel Webhook Xendit
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| class WebhookController extends Controller | |
| { | |
| /** | |
| * Paymentlink: https://developers.xendit.co/api-reference/#invoice-callback | |
| * Virtual Account: https://developers.xendit.co/api-reference/#virtual-account-callback | |
| * Retail: https://developers.xendit.co/api-reference/#fixed-payment-code-callback | |
| * QRCode: https://developers.xendit.co/api-reference/id/#callback-pembayaran-qr | |
| */ | |
| public function xendit(Request $request): JsonResponse | |
| { | |
| Log::channel('xendit')->error('Xendit Webhook: ' . $request->all()); | |
| $signature = $request->header('X-Callback-Token'); | |
| $expectedSignature = env('XENDIT_WEBHOOK_SECRET'); | |
| if (is_null($signature)) { | |
| return response()->json(['message' => 'Invalid signature'], 403); | |
| } | |
| if (! hash_equals($signature, $expectedSignature)) { | |
| return response()->json(['message' => 'Invalid signature'], 403); | |
| } | |
| $id = $request->id ?: null; | |
| $external_id = $request->external_id ?: null; | |
| if ($id) { | |
| XenditJob::dispatch($id, $request->all(), $external_id); | |
| } | |
| return response()->json('OK'); | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <?php | |
| class XenditJob implements ShouldQueue | |
| { | |
| use Dispatchable, InteractsWithQueue, Queueable, SerializesModels; | |
| /** | |
| * Create a new job instance. | |
| */ | |
| public function __construct(public string $id, public array $payload, public ?string $external_id) | |
| { | |
| // | |
| } | |
| /** | |
| * Execute the job. | |
| */ | |
| public function handle(): void | |
| { | |
| // | |
| $row = Invoice::find($this->external_id); | |
| if (isset($row) && ($row->payment_status == PaymentStatusEnum::UNPAID->value)) { | |
| $row->update([ | |
| 'payment_status' => PaymentStatusEnum::PAID->value, | |
| 'payment_method' => PaymentMethodEnum::PAYMENT_GATEWAY->value, | |
| ]); | |
| $this->xenditUpdate(id: $this->id, payload: $this->payload); | |
| } else { | |
| Telegram::sendMessage([ | |
| 'chat_id' => env('TELEGRAM_ID_PERSONAL'), | |
| 'text' => "XenditJob, external_id: {$this->external_id} NOT FOUND on Invoice" | |
| ]); | |
| } | |
| } | |
| protected function xenditUpdate(string $id, array $payload): void | |
| { | |
| $paymentLink = XenditPaymentlink::where('xendit_id', $id)->first(); | |
| if ($paymentLink) { | |
| $paymentLink | |
| ->update([ | |
| 'status' => $payload['status'], | |
| // 'amount' => $payload['amount'], | |
| // 'description' => $payload['description'], | |
| 'created' => $payload['created'], | |
| 'updated' => $payload['updated'], | |
| 'transaction_timestamp' => $payload['paid_at'], | |
| 'payload' => json_encode($payload), | |
| ]); | |
| } | |
| if (isset($payload['callback_virtual_account_id'])) { | |
| $virtualAccount = XenditVirtualaccount::where('xendit_id', $payload['callback_virtual_account_id'])->first(); | |
| if ($virtualAccount) { | |
| $virtualAccount | |
| ->update([ | |
| 'bank_code' => $payload['bank_code'], | |
| // 'name' => $payload['name'], | |
| // 'is_closed' => $payload['is_closed'], | |
| // 'is_single_use' => $payload['is_single_use'], | |
| 'status' => 'PAID', | |
| // 'expected_amount' => $payload['amount'], | |
| // 'expiration_date' => $payload['expiration_date'], | |
| 'transaction_timestamp' => $payload['transaction_timestamp'], | |
| 'payload' => json_encode($payload), | |
| ]); | |
| } | |
| } | |
| if (isset($payload['fixed_payment_code_id'])) { | |
| $retail = XenditRetail::where('xendit_id', $payload['fixed_payment_code_id'])->first(); | |
| if ($retail) { | |
| $retail | |
| ->update([ | |
| 'payment_code' => $payload['payment_code'], | |
| // 'retail_outlet_name' => $payload['retail_outlet_name'], | |
| // 'prefix' => $payload['prefix'], | |
| // 'type' => $payload['type'], | |
| // 'expected_amount' => $payload['amount'], | |
| // 'is_single_use' => $payload['is_single_use'], | |
| 'status' => $payload['status'], | |
| // 'expiration_date' => $payload['expiration_date'], | |
| 'transaction_timestamp' => $payload['transaction_timestamp'], | |
| 'payload' => json_encode($payload), | |
| ]); | |
| } | |
| } | |
| $qrcode = XenditQrcode::where('xendit_id', $payload['qr_code']['id'])->first(); | |
| if ($qrcode) { | |
| $qrcode | |
| ->update([ | |
| 'status' => $payload['status'], | |
| 'transaction_timestamp' => $payload['created'], | |
| 'payload' => json_encode($payload), | |
| ]); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment