Internal Reference · Demo

Beyond Games — Payment System

How the booking + payment flow works, end to end.

The goal

Beyond Games requires payment at the time of booking, so an appointment is only real once it's been paid for. This system books the appointment as unconfirmed, takes payment, and only then flips it to confirmed.

The pieces

  • GHL Funnel — 4 pagesCalendar (book), Pay Form (sends to payment), Success, Failure.
  • Cloudflare Worker — "beyond-games-pay"The secure middleman. Signs the payment request with the shared secret, then receives the bank's response, verifies it, confirms the appointment, and sends the customer onward.
  • Scotiabank / Fiserv IPG ConnectThe payment gateway. Hosts the actual card page — we never touch card data.
  • HighLevel Workflow — inbound webhookReceives the success signal, matches the contact by email, and updates their most recent appointment to confirmed.

The flow, step by step

  1. Customer books on the calendar page. Appointment is created as unconfirmed.
  2. GHL redirects to the pay page with the customer's email and contactId in the URL: /pay-form-page?email={{contact.email}}&contactId={{contact.id}}
  3. The pay page calls the Worker. The Worker builds the signed request (email + contactId are signed so they come back later) and returns it.
  4. The pay page auto-submits the signed request to the bank's hosted payment page. Customer pays.
  5. The bank sends its result to the Worker — not the GHL page directly, because GHL blocks outside POSTs. The Worker catches it.
  6. The Worker verifies success: approval_code starting with Y. (We check the code, not the status text, because the gateway returns status in Spanish — "APROBADO" — but the Y is language-independent.)
  7. On success, the Worker signals HighLevel, then redirects the customer to the real success page. On failure, it redirects to the failure page and does not confirm.
  8. The HighLevel workflow finds the contact by email and updates their most recent appointment to confirmed.

Why email is the match key

GHL's calendar can't reliably pass its internal appointment ID to the redirect, so the email threads the whole flow: booking → payment → confirmation. HighLevel's "Update Appointment Status" action confirms the contact's most recent appointment — which, for a book-and-pay-immediately flow, is the one they just booked.

Going live — what changes

These are set for testing. Before launch, in the Worker:

  • Currency: 840 (USD test) → 780 (TTD live)
  • Gateway URL: swap the test URL for the live production URL (from the bank)
  • Shared secret + store name: the bank issues new production values after certification
  • CORS: tighten Access-Control-Allow-Origin from * to the real domain
  • Amount: if pricing varies, pass a service code and look up the real price in the Worker — never a raw dollar amount in the URL

Test cards

  • Approve: 5204740000002745 (any future expiry, any CVV)
  • Approve: 4147463011110083
  • Decline: 5426064000424979 — amount 7.45, expiry 12/25
  • Use even amounts on test — odd amounts can auto-decline.

Key principle

Anything that must be trusted lives in the Worker, never in the browser. The shared secret, the real price, the success check. The Worker is the safe room; the URL is a public hallway. If you're tempted to put something sensitive in the page or the URL, put it in the Worker instead.

Outstanding before launch: run the decline test for certification, confirm the pricing model, and apply the go-live switches once production credentials arrive.