Take payments
Checkout in the browser and in apps
harbour.js for a popup checkout, and what to do inside a mobile app.
harbour.js opens the same hosted checkout in a popup, so the customer never leaves your page. It is served by Harbour, not by the SDK, and it takes a public key. Card details are still entered on the provider's own page: the popup hands off to them.
With a public key
<script src="https://pay.harbour.africa/v1/harbour.js"></script>
<script>
document.getElementById('pay').addEventListener('click', function () {
Harbour.checkout({
key: 'hb_pk_live_your_public_key',
amount: 1200000, // kobo
email: 'funke@example.ng',
reference: 'order-1043', // your reference, optional
onSuccess: function (result) {
// result.reference === "HB-88413": hand it to your server and verify there
fetch('/orders/1043/confirm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ reference: result.reference })
}).then(function () { window.location = '/thanks'; });
},
onClose: function () { /* the customer closed the popup */ },
onError: function (err) { console.error(err.message); }
});
});
</script>
Restrict the public key to your own domains in Settings → Developers so it cannot be used from someone else's site.
PHP
<?php
// POST /orders/1043/confirm — the browser is not a source of truth
$checkout = Harbour::verify($request->json('reference'));
if ($checkout->isPaid() && $checkout->amountKobo === $order->total_kobo) {
$order->markPaid();
}
With a checkout token
If you would rather create the charge on your server (so the amount is never in the page), pass the checkout token
instead of the public key. Checkout::inlinePayload() returns exactly what harbour.js needs:
PHP
<?php $payload = $checkout->inlinePayload(); ?>
<script src="https://pay.harbour.africa/v1/harbour.js"></script>
<script>
Harbour.checkout(Object.assign(
<?= json_encode($payload, JSON_HEX_TAG | JSON_HEX_AMP | JSON_HEX_APOS | JSON_HEX_QUOT) ?>,
{ onSuccess: confirmOnServer }
));
</script>
The payload carries token, amount, email and reference — no key,
and nothing a customer can usefully edit.
When popups are blocked
harbour.js falls back to a full-page redirect on its own. The customer comes back to the charge's
return_url with ?reference=HB-…&status=succeeded, so your return page must handle that case
too. With the public-key form, set a return URL by creating the charge server side first, or handle
onClose by reloading the order page.
Inside a mobile app
Do not embed the checkout in a WebView: providers reject some card flows there, and 3-D Secure can break. Open
checkout_url in the system browser component instead — Chrome Custom Tabs on Android,
SFSafariViewController on iOS — with return_url set to your deep link.
PHP
<?php
// your server, when the app asks to start a payment
$checkout = Harbour::charge($order->total_kobo, $user->email, [
'reference' => $order->reference,
'return_url' => 'myapp://harbour/return', // your app's deep link
]);
return response()->json([
'checkout_url' => $checkout->checkoutUrl, // open this in a Custom Tab / SFSafariViewController
'reference' => $checkout->reference,
]);
Harbour appends ?reference= and &status= to the deep link. When your app is woken by it,
call your own server to verify; the app never talks to Harbour with a secret key.