Forhopp

ForhoppPay Connect — Hosted Checkout

Feature Reference: FR-9 | Service: MerchantCheckoutService, SandboxPaymentSimulator | Controller: ConnectCheckoutController


1. Overview

ForhoppPay Connect provides hosted checkout pages at pay.forhopp.com/checkout/{charge_id}. These are publicly accessible pages where end customers complete payments. The checkout service handles page data retrieval, payment method selection, sandbox simulation, and redirect logic.


2. Checkout URL

https://pay.forhopp.com/checkout/{charge_id}

Generated automatically when a charge is created. The URL is included in the charge response as checkout_url.


3. Endpoints

Method Path Auth Description
GET /checkout/{chargeId} None (public) Get checkout page data
POST /checkout/{chargeId}/complete None (public) Complete payment
POST /checkout/{chargeId}/cancel None (public) Cancel checkout

These endpoints are publicly accessible — they are used by end customers, not merchants.


4. Checkout Page Data

4.1 Request

GET /checkout/ch_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6

4.2 Response

{
  "chargeId": "ch_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6",
  "amountCents": 5000,
  "currency": "USD",
  "formattedAmount": "$50.00",
  "description": "Order #12345",
  "merchantName": "Acme Corp",
  "merchantLogoUrl": "https://acme.com/logo.png",
  "merchantBrandColor": "#4F46E5",
  "merchantSupportEmail": "support@acme.com",
  "returnUrl": "https://acme.com/success",
  "cancelUrl": "https://acme.com/cancel",
  "availablePaymentMethods": ["card", "paypal"],
  "status": "pending",
  "valid": true,
  "expiresAt": 1711900800,
  "testMode": false,
  "metadata": { "order_id": "12345" }
}

4.3 Invalid States

The checkout page returns valid: false with an error message for:

Condition Error Message
Invalid charge ID format "Invalid charge ID format"
Charge not found "Charge not found"
Session expired (24h) "This checkout session has expired"
Already authorized/captured "This payment has already been processed"
Failed "This payment has failed"
Voided "This payment has been cancelled"
Refunded "This payment has been refunded"
Disputed "This payment is under dispute"

5. Checkout Completion

5.1 Request

POST /checkout/ch_xxx/complete
Content-Type: application/json

{
  "paymentMethod": "card",
  "paymentMethodDetails": {
    "card_number": "4242424242424242",
    "exp_month": 12,
    "exp_year": 2030,
    "cvc": "123"
  }
}

5.2 Success Response

{
  "success": true,
  "chargeId": "ch_xxx",
  "status": "authorized",
  "redirectUrl": "https://merchant.com/success",
  "testMode": false
}

5.3 Failure Response

{
  "success": false,
  "chargeId": "ch_xxx",
  "status": "failed",
  "redirectUrl": "https://merchant.com/cancel",
  "errorCode": "generic_decline",
  "errorMessage": "Your card was declined.",
  "testMode": true
}

5.4 Processing Flow

1. Validate charge ID format
2. Find charge in database
3. Check not expired (24h)
4. Check status is PENDING
5. Validate payment method ∈ {card, paypal}
6. Route by environment:
   ├─ TEST → SandboxPaymentSimulator
   └─ LIVE → Real payment processing
7. Update charge with payment details
8. Dispatch webhook (charge.completed or charge.failed)
9. Return redirect URL

6. Sandbox Payment Simulation

When the charge environment is TEST, the SandboxPaymentSimulator handles payment processing. No real payments are ever made for test keys.

6.1 Test Card Numbers

Card Number Result Error Code
4242424242424242 Success
4000000000000002 Declined generic_decline
4000000000009995 Declined insufficient_funds
4000000000000069 Declined expired_card
4000000000000127 Declined incorrect_cvc
4000000000000119 Error processing_error
4000000000003220 Requires 3DS authentication_required

Any other card starting with 4 and length ≥ 13 simulates success.

6.2 Test PayPal Accounts

Email Result
test-success@sandbox.paypal.com Success
test-decline@sandbox.paypal.com Declined
test-pending@sandbox.paypal.com Pending

Any other valid email simulates success in sandbox.

6.3 Sandbox Response Details

Successful sandbox payments include simulated payment method details:

{
  "type": "card",
  "brand": "visa",
  "last4": "4242",
  "exp_month": 12,
  "exp_year": 2030,
  "funding": "credit",
  "country": "US"
}

Transaction IDs are prefixed with sandbox_txn_.


7. Checkout Cancellation

POST /checkout/ch_xxx/cancel

Returns the cancel_url for redirect. The charge status is not changed (remains PENDING and will eventually expire).


8. Redirect Behavior

Outcome Redirect To
Payment succeeds return_url from charge
Payment fails cancel_url from charge
Customer cancels cancel_url from charge
Session expired cancel_url from charge

9. Merchant Branding

The checkout page displays merchant branding from merchant_profiles:

Field Usage
business_name Displayed as merchant name
logo_url Merchant logo on checkout page
brand_color Primary color for buttons/accents
support_email Contact link on checkout page

If no merchant profile exists, defaults to "Merchant" with no logo.


10. Currency Formatting

Currency Format Example
USD $50.00
EUR €50.00
GBP £50.00
CAD CA$50.00
AUD A$50.00
JPY ¥5000 (no decimals)
CHF CHF 50.00

11. Webhook Dispatch

After checkout completion, webhooks are dispatched asynchronously:

  • Success → charge.completed event
  • Failure → charge.failed event

Webhook dispatch errors are caught and logged but do not affect the checkout response.


12. Correctness Properties

# Property Validates
17 Success redirects to return_url; cancel redirects to cancel_url FR-9.3, FR-9.4
18 No real payments for test keys; environment=test marking FR-10.1
19 Test card 4242... succeeds; 4000...0002 declines; 4000...9995 insufficient funds FR-10.2

Last Updated: March 2026

Was this page helpful?

Edit this page