Skip to content

feat: embedded iframe checkout option (one-off payments) - #2

Merged
MHJaafar9869 merged 1 commit into
mainfrom
feat/embedded-iframe-checkout
Aug 6, 2026
Merged

feat: embedded iframe checkout option (one-off payments)#2
MHJaafar9869 merged 1 commit into
mainfrom
feat/embedded-iframe-checkout

Conversation

@MHJaafar9869

@MHJaafar9869 MHJaafar9869 commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds an Embedded Checkout gateway option that renders the GetPayIn hosted checkout in an iframe on the merchant's site instead of redirecting to it. It's a config toggle (No/Yes) alongside the existing options (Payment Action, Installments, etc.).

How it works

When Embedded Checkout = Yes for a one-off payment:

  1. createCheckoutUrl() sends iframe=1 in the v2 init POST body. The backend (API/V2/.../ExternalPaymentIntegrationController::generateInvoice) reads iframe from the body and bakes iframe=1 into the signed checkout_url before signing — so this is the correct trigger. (Appending &iframe=1 to the returned URL would break the signed middleware.)
  2. beginTransaction() branches to renderIframe() (the original redirect path is preserved as renderRedirect()).
  3. renderIframe() embeds the checkout and installs a message listener matching the backend's paylink_payment postMessage contract — {type, success, invoice_id, invoice_status, message}. On success it moves the top window to return_url, on failure to error_url.

Safety / correctness guards

  • Origin-checked postMessage — the listener rejects any message whose origin isn't the configured GetPayIn host, so a foreign frame can't spoof a "paid" result. (The checkout also targets its postMessage at the integration's registered Origin, so it's only delivered to a parent on that exact origin.)
  • Recurring excludediframeEnabled() returns false for subscriptions, because the recurring init endpoint does not sign the iframe flag. Those keep redirecting.
  • allow="payment *" on the iframe for Apple/Google Pay in the cross-origin frame, plus a target="_top" fallback link for browsers that block third-party frames.

Verification

  • php -l clean across all files; signing golden-vector tests pass (ALL SIGNATURE CHECKS PASSED).
  • Contract verified statically against the GetPayIn backend (init endpoint, checkout page, SecurityHeadersMiddleware frame-ancestors, blade postMessage payloads).

⚠️ Not yet tested end-to-end on a live Vik install (the framework isn't on the dev box). Recommend a live test booking with Embedded Checkout = Yes before release — in particular confirming VikBooking populates return_url at beginTransaction time; if empty for a given component, the listener falls back to reloading the top window rather than landing on the order page.

Bumps version 1.2.1 → 1.3.0; documents the option in README, CHANGELOG, and the .pot template.

Add an "Embedded Checkout" gateway option that renders the GetPayIn hosted
checkout in an iframe instead of redirecting to it.

When enabled, the plugin sends iframe=1 on the v2 init call so the returned
Laravel-signed checkout_url carries the flag (appending it to the URL would
break the signed middleware), embeds that URL, and listens for the checkout's
signed paylink_payment postMessage to move the top window to the shop's return
(success) or error (failure) URL. The listener accepts messages only from the
configured GetPayIn origin, so a foreign frame cannot spoof an outcome.

Applies to one-off payments only; recurring subscriptions always redirect
because the recurring init endpoint does not sign the iframe flag.

Bumps version to 1.3.0 and documents the option in the README, CHANGELOG, and
the .pot translation template.
@MHJaafar9869
MHJaafar9869 force-pushed the feat/embedded-iframe-checkout branch from 4f2ede5 to 67007fc Compare August 6, 2026 09:55
@MHJaafar9869
MHJaafar9869 merged commit f4c31b8 into main Aug 6, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant