Collect MoMo MSISDN and open waiting page on Pay.
Deploy Ladill Mini / deploy (push) Successful in 42s
Deploy Ladill Mini / deploy (push) Successful in 42s
Payment QR was still Paystack-only: no customer_phone and mobile iframe sheet. Require MoMo number, pass it to Ladill Pay, and full-page redirect for mtn_momo waiting URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -27,6 +27,7 @@ class PaymentController extends Controller
|
||||
|
||||
$validated = $request->validate([
|
||||
'amount' => 'required|numeric|min:0.01',
|
||||
'customer_phone' => 'required|string|max:32',
|
||||
]);
|
||||
|
||||
try {
|
||||
@@ -40,7 +41,10 @@ class PaymentController extends Controller
|
||||
}
|
||||
|
||||
if ($request->expectsJson()) {
|
||||
return response()->json(['checkout_url' => $result['checkout_url']]);
|
||||
return response()->json([
|
||||
'checkout_url' => $result['checkout_url'],
|
||||
'provider' => $result['provider'] ?? null,
|
||||
]);
|
||||
}
|
||||
|
||||
return redirect()->away($result['checkout_url']);
|
||||
|
||||
@@ -25,8 +25,8 @@ class MiniPaymentService
|
||||
) {}
|
||||
|
||||
/**
|
||||
* @param array{amount: float} $data
|
||||
* @return array{payment: MiniPayment, checkout_url: string}
|
||||
* @param array{amount: float, customer_phone?: string} $data
|
||||
* @return array{payment: MiniPayment, checkout_url: string, provider: string}
|
||||
*/
|
||||
public function initiate(QrCode $qrCode, array $data): array
|
||||
{
|
||||
@@ -39,6 +39,11 @@ class MiniPaymentService
|
||||
throw new RuntimeException('Enter an amount greater than zero.');
|
||||
}
|
||||
|
||||
$customerPhone = trim((string) ($data['customer_phone'] ?? ''));
|
||||
if ($customerPhone === '') {
|
||||
throw new RuntimeException('Enter your MoMo number to pay.');
|
||||
}
|
||||
|
||||
$qrCode->loadMissing('user');
|
||||
$amountMinor = (int) round($amountGhs * 100);
|
||||
$reference = 'MINP-'.strtoupper(Str::random(16));
|
||||
@@ -52,7 +57,7 @@ class MiniPaymentService
|
||||
'currency' => $qrCode->content()['currency'] ?? 'GHS',
|
||||
'payer_name' => null,
|
||||
'payer_email' => null,
|
||||
'payer_phone' => null,
|
||||
'payer_phone' => $customerPhone,
|
||||
'payer_note' => null,
|
||||
'status' => MiniPayment::STATUS_PENDING,
|
||||
'payment_reference' => null,
|
||||
@@ -64,6 +69,7 @@ class MiniPaymentService
|
||||
'source_service' => 'mini',
|
||||
'source_ref' => (string) $qrCode->id,
|
||||
'callback_url' => LadillLink::path($qrCode->short_code, 'pay/callback'),
|
||||
'customer_phone' => $customerPhone,
|
||||
'line_items' => [
|
||||
[
|
||||
'name' => $businessName,
|
||||
@@ -93,6 +99,7 @@ class MiniPaymentService
|
||||
return [
|
||||
'payment' => $payment->fresh(),
|
||||
'checkout_url' => $checkoutUrl,
|
||||
'provider' => (string) ($payOrder['provider'] ?? ''),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
|
||||
namespace App\Services\Pay;
|
||||
|
||||
use Illuminate\Http\Client\Response;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use RuntimeException;
|
||||
|
||||
/**
|
||||
* Client for the platform Ladill Pay HTTP API — checkout and settlement for
|
||||
* merchant↔buyer money. Paystack is the processor today; settlement lands in
|
||||
* the one UserWallet via Platform Billing.
|
||||
* merchant↔buyer money. Provider is configured on the platform
|
||||
* (PAY_DEFAULT_PROVIDER); settlement lands via wallet or direct MoMo.
|
||||
*/
|
||||
class PayClient
|
||||
{
|
||||
@@ -25,9 +27,8 @@ class PayClient
|
||||
public function createCheckout(array $payload): array
|
||||
{
|
||||
$res = Http::withToken($this->token())->acceptJson()->timeout(15)->post($this->base().'/checkouts', $payload);
|
||||
$res->throw();
|
||||
|
||||
return (array) $res->json();
|
||||
return $this->jsonOrFail($res, 'Could not start checkout. Please try again.');
|
||||
}
|
||||
|
||||
public function verify(string $reference): array
|
||||
@@ -35,16 +36,36 @@ class PayClient
|
||||
$res = Http::withToken($this->token())->acceptJson()->timeout(15)->post($this->base().'/checkouts/verify', [
|
||||
'reference' => $reference,
|
||||
]);
|
||||
$res->throw();
|
||||
|
||||
return (array) $res->json();
|
||||
return $this->jsonOrFail($res, 'Could not verify payment. Please try again.');
|
||||
}
|
||||
|
||||
public function show(string $reference): array
|
||||
{
|
||||
$res = Http::withToken($this->token())->acceptJson()->timeout(10)->get($this->base().'/orders/'.$reference);
|
||||
$res->throw();
|
||||
|
||||
return $this->jsonOrFail($res, 'Could not load payment order.');
|
||||
}
|
||||
|
||||
/** @return array<string,mixed> */
|
||||
private function jsonOrFail(Response $res, string $fallback): array
|
||||
{
|
||||
if ($res->failed()) {
|
||||
throw new RuntimeException($this->errorMessage($res, $fallback));
|
||||
}
|
||||
|
||||
return (array) $res->json();
|
||||
}
|
||||
|
||||
private function errorMessage(Response $res, string $fallback): string
|
||||
{
|
||||
$message = $res->json('error') ?? $res->json('message');
|
||||
if (is_array($message)) {
|
||||
$message = collect($message)->flatten()->first();
|
||||
}
|
||||
|
||||
$message = trim((string) ($message ?? ''));
|
||||
|
||||
return $message !== '' ? $message : $fallback;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user