Create Payment
Create a new payment session and get a checkout URL to redirect your customer.
Request Body
| Field | Type | Required | Description |
|---|---|---|---|
amount | integer | Required | Amount in cents (5000 = $50.00). Integer from 1 to 99999999. |
currency | string | Required | 3-letter code: usd or cad (case-insensitive). Any other currency returns 400 INVALID_REQUEST. |
successUrl | string | Required | Redirect URL after successful payment. Must start with http:// or https:// and be 10 to 2000 characters. The first {transactionId} in the URL is replaced with the UltraPay transaction ID, e.g. https://yoursite.com/thank-you?tx={transactionId} |
cancelUrl | string | Required | Redirect URL if customer cancels. Same URL rules as successUrl, and the first {transactionId} is replaced the same way. |
customerEmail | string | Required | Customer's email address (valid email, max 255 characters). If it is omitted, payment creation fails with 502 STRIPE_ERROR ("customerEmail is required"). |
customerName | string | Optional | Customer's full name (max 255 characters) |
description | string | Optional | Payment description (shown in dashboard), max 1000 characters |
webhookUrl | string | Optional | URL to receive payment status updates. Same URL rules as successUrl. |
metadata | object | Optional | Custom key-value data (e.g., order ID). Maximum 20 keys; more returns 400 INVALID_REQUEST. Keys starting with an underscore (_) are reserved for internal use: they may be overwritten and are never returned by Get Payment. A description key is returned as the top-level description field, so use the description field above instead. |
Requests that leave out amount, currency, successUrl or cancelUrl, or that break the type, range, length, format, currency or metadata key-count limits above, are rejected with 400 INVALID_REQUEST. A missing customerEmail fails with 502 instead, as noted above. Fields not listed above are ignored.
Example Request
{"amount": 9999,"currency": "usd","customerEmail": "john@example.com","customerName": "John Doe","description": "Order #12345","successUrl": "https://yoursite.com/thank-you?order=12345","cancelUrl": "https://yoursite.com/checkout","webhookUrl": "https://yoursite.com/api/webhooks/ultrapay","metadata": {"orderId": "12345","productName": "Premium Plan"}}
Success Response
{"success": true,"data": {"transactionId": "550e8400-e29b-41d4-a716-446655440000","paymentUrl": "https://secure-checkout.example.com/api/pay/ss_pay_3f9c2a7b1e4d4c8a9b6e0f12","sessionId": "cs_live_a1b2c3d4...","expiresAt": "2025-01-01T12:30:00.000Z"}}
Response Fields
| Field | Description |
|---|---|
transactionId | UltraPay transaction ID (save this!) |
paymentUrl | Hosted payment link on an UltraPay payment domain - redirect customer here (it forwards to Stripe Checkout). Don't assume the host is checkout.stripe.com. |
sessionId | Stripe Checkout Session ID |
expiresAt | When the payment link expires (usually 30 min) |
URL Privacy
Your
successUrlandcancelUrlare hidden from Stripe. After payment, customers are redirected through our servers first, then to your URL. This keeps your domain private.
Your successUrl, cancelUrl and webhookUrl must use a publicly reachable host. If the host is localhost, 127.0.0.1, 0.0.0.0, 169.254.169.254, starts with 10., 172. or 192.168., or ends in .internal or .local, the payment is still created, but the customer is sent to https://ultrapay.cc instead of your page and no webhook is delivered to that URL. Customers are also sent to https://ultrapay.cc if your redirect URL is not a valid http(s) URL. To test locally, use a public tunnel URL (for example ngrok).
Important: Never Use Iframes
⚠️ Warning: Never embed the
paymentUrlin an iframe. Always use a full page redirect.
Payment URLs must be opened via window.location.href or a standard link. Iframes are blocked because:
- Stripe actively blocks checkout pages from loading in iframes
- Most browsers block third-party cookies in iframes, breaking payments
- Iframe embedding is a common phishing technique that payment processors prevent
- 3D Secure authentication cannot complete properly in an iframe
cURL Example
curl -X POST https://pay.ultrapay.cc/api/v1/payments/create \-H "X-Public-Key: upp_your_public_key" \-H "X-Secret-Key: ups_your_secret_key" \-H "Content-Type: application/json" \-d '{"amount": 5000,"currency": "usd","customerEmail": "customer@example.com","successUrl": "https://yoursite.com/thank-you","cancelUrl": "https://yoursite.com/checkout"}'