Embedded Checkout
By default, when you start a Neon checkout, the user is redirected to a Neon-hosted page. This helps ensure that you can remain PCI-compliant, since no sensitive data is entered on your site.
Alternatively, you can embed a Neon checkout in your website using an iframe and our JavaScript wrapper, @neonpay/js
. This lets your users complete their purchase without ever leaving your site (except in cases where a full-page redirect is required—more on that later).
1. Overview of the Flow
- Create a checkout on your server using your secret API key.
- Initialize the checkout on the client using your public client key.
- Mount the checkout into your page with a CSS selector.
- Listen for events such as a successful purchase.
- Handle return redirects from payment providers like PayPal.
2. Server: Create a Checkout
Use your server to create a checkout with your Neon API key by calling POST /checkout
:
import fetch from 'node-fetch';
export async function createCheckout() {
const YOUR_DOMAIN = 'http://localhost:3000';
const response = await fetch('https://api.neonpay.com/checkout', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Api-Key': process.env.NEON_API_KEY,
},
body: JSON.stringify({
items: [
{
sku: "BIG_SWORD",
name: "A big sword",
quantity: 1,
imageUrl: "https://placehold.co/600x400",
price: 299
}
],
returnUrl: "${YOUR_DOMAIN}/checkout",
accountId: "account-id",
languageLocale: "en-US",
playerCountry: "US",
currency: "USD"
}),
});
const data = await response.json();
return data.checkoutId;
}
items
contains the list of Item objects the user wants to purchase:
-
sku
is a unique code you can use to fulfill the purchase to the user's account. See Updating Your Game for more on the fulfillment process.name
is the display name of the item, localized to the player's preferred language.quantity
is the number of this item they want to purchase.price
is the price of the checkout, set to 100x the base unit of the currency. See Currencies for more details.- Optionally, you can set
imageUrl
to a link to an image that we can use to display the item during checkout. - Optionally, you can set
subtitle
andhighlightedSubtitle
to display additional information about the item.
returnUrl
is the URL you want the user to return to after completing a redirect-based payment flow. (More on this below.)languageLocale
andplayerCountry
are used to geo-locate the user and offer the appropriate translations and payment methods.languageLocale
is an IETF BCP 47 tag, e.g.en-US
, that's used to show the correct language for all text in the checkout form.playerCountry
is an ISO 3166-1 country code, e.g.US
, that's used to show the correct payment methods for the user, and also to ensure that the user's input payment method originates from the same country they are physically in.
accountId
is a unique account ID that's used to fulfill the purchase to the correct account. See Updating Your Game for more on the fulfillment process.- Optionally, you can set
externalReferenceId
andexternalMetadata
for use in the fulfillment process. Neon includes it in the purchase completion and in analytics events, but otherwise doesn't make use of it.
3. Client: Embed the Checkout
Add an empty mount point in your HTML:
<div id="checkout-container"></div>
Initialize and mount the checkout in your JavaScript, and listen for events:
import { init } from '@neonpay/js';
const client = init({ clientKey: 'your_client_key' });
function startCheckout(checkoutId, fromRedirect = false) {
const checkout = client.startEmbeddedCheckout({ checkoutId, fromRedirect });
checkout.mount('#checkout-container');
checkout.on('purchase.completed', ({ purchaseId }) => {
console.log('Checkout completed:', purchaseId);
// redirect to success page, show confirmation, etc.
});
}
const response = await fetch('/api/create-checkout');
const { checkoutId } = await response.json();
startCheckout(checkoutId);
Including @neonpay/js
@neonpay/js
You can include @neonpay/js
in one of two ways:
npm
(Recommended)
The recommended approach to including Neon.js in your project is by using the module loader provided as an npm
package. The package makes it easy to load the script tag in your application and includes TypeScript definitions. See the package documentation for details.
# npm
npm install --save @neonpay/js
# yarn
yarn add @neonpay/js
<script>
Tag
Alternatively, you can include the script tag directly on your site.
<script src="https://js.poweredbyneon.com/v1/neon.js"></script>
<script>
const neon = new window.Neon("{{client key}}");
console.log(neon);
</script>
In general, we recommend loading neon.js asynchronously, by adding async
or defer
to the <script>
tag. However, note that doing so will mean that the Neon
global may not be immediately available (since it's loaded only after Neon.js fully loads), so your scripts will need to handle the possibility that it's not yet defined.
4. Client: Handle Checkout Events
We emit two events that you can listen to, and optionally action on.
purchase.completed
purchase.completed
On payment success, we fire the purchase.completed
event with the purchase ID of the completed checkout. You can use this ID to call the GET /purchase
endpoint to show the user an order summary, or you can redirect back to the game or your store.
checkout.amounts_updated
checkout.amounts_updated
Whenever any of the checkout amounts change, we fire the checkout.amount_updated
event with the following data:
itemTotal
: the total of the items in the checkout (item.price
xitem.quantity
for each item in the cart)subtotalAmount
: the total amount minus taxestaxAmount
: the sales taxes on this transactiontotalAmount
: the total amount the user will be charged
5. Client: Handle Redirect-Based Payment Methods
Some payment methods (like PayPal) require a full-page redirect. Neon handles this automatically when the user selects such a method. Here's how it works:
- We redirect the user from your site to the payment provider.
- After payment, the user is returned to the
returnUrl
you supplied. - On that return page, you'll receive the checkout ID as
?checkout_id=...
in the query string. - Pass that ID to
startEmbeddedCheckout
withfromRedirect: true
.
We’ll read the status of the checkout and either:
- Emit the same
purchase.completed
event as before, or - Show an error state inside the iframe.
(There’s no need to create a new checkout in this case.)
Here's an example integration, modifying checkout.js
above to handle both flows:
import { init } from '@neonpay/js';
const client = init({ clientKey: 'your_client_key' });
function startCheckout(checkoutId, fromRedirect = false) {
const checkout = client.startEmbeddedCheckout({ checkoutId, fromRedirect });
checkout.mount('#checkout-container');
checkout.on('purchase.completed', ({ purchaseId }) => {
console.log('Checkout completed:', purchaseId);
// redirect to success page, show confirmation, etc.
});
}
// Look for a checkout ID in the URL (e.g., after a redirect)
const urlParams = new URLSearchParams(window.location.search);
const redirectedCheckoutId = urlParams.get('checkoutId');
if (redirectedCheckoutId) {
startCheckout(redirectedCheckoutId, true);
} else {
const response = await fetch('/api/create-checkout');
const { checkoutId } = await response.json();
startCheckout(checkoutId);
}
Note that in this case, returnUrl
should be set to the same URL as the page that hosts the checkout iframe. You can also set it to a different, more specific URL; however, you'll need to handle the checkout the same way as described above.
Updated 1 day ago