Snap JavaScript SDK (v2) — Quick Start Guide (Expanded Checkout + Get Approved)
Overview
Snap’s JavaScript SDK adds a secure "Pay with Snap" checkout option. The SDK opens a secure application window and returns results through callbacks (for example, an applicationId when approved). This guide includes practical samples for handling checkout approval and the "Get Approved" marketing treatment flow.
- Your server exchanges Client ID/Secret for a short-lived bearer token (~24 hours) and returns it to your frontend via a secure endpoint.
- Your frontend loads the Snap SDK v2, calls
snap.init(token), then renders the checkout mark and checkout button. - On button click, call
actions.launchCheckout(transaction). Handle events likeonApproved,onDenied,onCanceled, andonError.
Step 1 — Verify requirements
- Snap Developer credentials: Client ID and Client Secret.
- A secure server to keep secrets and request tokens.
- Frontend where the SDK will be loaded and the button will be shown.
- Serve pages over https in production. Localhost is acceptable for development.
Step 2 — Choose environment
audience: https://api-release.snapfinance.com/platform/v1audience: https://api.snapfinance.com/platform/v1Step 3 — Get an access token
Request a server token and cache it. The token typically expires in ~24 hours. Rotate slightly before expiry.
Do not embed client_secret in browser code. Keep it on the server.
3.1 Token request (example cURL)
curl --location 'https://auth-sandbox.snapfinance.com/oauth/token' \
--header 'Content-Type: application/json' \
--data '{
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET",
"audience": "https://api-release.snapfinance.com/platform/v1",
"grant_type": "client_credentials"
}'
3.2 Server endpoint (Node/Express example)
// server.js (minimal)
const express = require('express');
const fetch = require('node-fetch');
const app = express();
app.get('/api/snap/token', async (req, res) => {
const resp = await fetch('https://auth-sandbox.snapfinance.com/oauth/token', {
method:'POST',
headers:{ 'Content-Type':'application/json' },
body: JSON.stringify({
client_id: process.env.SNAP_CLIENT_ID,
client_secret: process.env.SNAP_CLIENT_SECRET,
audience: 'https://api-release.snapfinance.com/platform/v1',
grant_type: 'client_credentials'
})
});
const json = await resp.json();
res.json({ access_token: json.access_token, expires_in: json.expires_in });
});
app.listen(3000);
Step 4 — Add the Web SDK (v2)
4.1 Include script
<!-- Sandbox -->
<script src="https://js.snapfinance.com/sandbox/v2/snap-sdk.js"></script>
<!-- Production -->
<!-- <script src="https://js.snapfinance.com/v2/snap-sdk.js"></script> -->
4.2 Initialize (frontend)
<script>
// Fetch token from your server, then:
const { access_token } = await fetch('/api/snap/token').then(r => r.json());
snap.init(access_token);
</script>
Step 5 — Build the transaction object
Money fields must be strings with two decimals and quantity must be an integer. Use a unique orderId.
// Build transaction (client-side)
function toMoney(v){ return Number(v).toFixed(2); }
function buildTransaction(items, tax, shipping, discounts){
const subtotal = items.reduce((s,i)=> s + Number(i.price)*Number(i.quantity), 0);
const total = subtotal + Number(tax) + Number(shipping) - Number(discounts);
return {
customerInformation: {
email: "",
firstName: "",
lastName: "",
billingAddress: {
streetAddress: "",
city: "",
state: "",
country: "",
postalCode: "",
unit: ""
}
},
cartInformation: {
currencyCode: "USD",
taxAmount: toMoney(tax),
shippingAmount: toMoney(shipping),
discountAmount: toMoney(discounts),
totalAmount: toMoney(total),
orderId: "ORD-" + Date.now(),
items: items.map(i => ({
price: toMoney(i.price),
itemId: i.itemId,
description: i.description,
sku: i.sku,
quantity: Number(i.quantity),
leasable: Boolean(i.leasable)
})),
shippingAddress: { /* same as billing or separate */ }
}
};
}
Step 6 — Render mark and button (checkout)
Render mark and button after snap.init(token). Do not render before initialization.
<div id="snap-checkout-mark"></div>
<div id="snap-checkout-button"></div>
<input id="applicationId" placeholder="Application ID will appear here" readonly />
6.1 Ensure initialization
function ensureInit(token){
if(!window.snap) throw new Error('Snap SDK missing');
snap.init(token);
}
6.1 Checkout — robust sample
This sample shows a safe click handler, retry on "not initialized", and approved handling that persists to your server for verification.
<script>
const appIdEl = document.getElementById('applicationId');
function renderMark(){
snap.checkoutMark({ style:{ color:'dark', height:40 } }).render('snap-checkout-mark');
}
function renderButton(items, tax, shipping, discounts){
const txBuilder = () => buildTransaction(items, tax, shipping, discounts);
const onClick = (data, actions) => {
const tx = txBuilder();
return actions.launchCheckout(tx).catch(err => {
if (/not initialized/i.test((err && err.message) || '')) {
try { snap.init(window.__SNAP_TOKEN__ || ''); } catch(_) {}
return actions.launchCheckout(tx);
}
throw err;
});
};
snap.checkoutButton({
style:{ color:'dark', shape:'pill', height:42 },
onClick: onClick,
onApproved: async (data) => {
console.log('Approved callback', data);
if (data && data.applicationId) {
appIdEl.value = data.applicationId;
await fetch('/api/snap/verify-approval', {
method:'POST',
headers:{ 'Content-Type':'application/json' },
body: JSON.stringify({ applicationId: data.applicationId, orderId: data.orderId })
});
}
},
onDenied: (data) => console.warn('Denied', data),
onCanceled: (data) => console.info('Canceled', data),
onNotification: (data) => console.info('Notification', data),
onError: (data) => console.error('Error', data)
}).render('snap-checkout-button');
}
</script>
6.2 Server verify approved application
Once approved, verify the applicationId server-side via Snap APIs or your internal reconciliation logic.
// Example express endpoint to reconcile an applicationId
app.post('/api/snap/verify-approval', express.json(), async (req, res) => {
const { applicationId, orderId } = req.body;
// Protect and make idempotent. Persist and reconcile as required.
await saveApprovalToDb({ applicationId, orderId, receivedAt: Date.now() });
res.sendStatus(200);
});
Store applicationId with your orderId and reconcile server-side for reporting and settlement.
6.3 SDK events (detailed)
Use these callbacks to manage UI states, capture outcomes, and reconcile approvals.
| Event | When it fires | What it means | Recommended handling |
|---|---|---|---|
onClick |
User clicks the Snap button | SDK provides an actions object used to start checkout |
Build the final transaction, prevent duplicate clicks, call actions.launchCheckout(tx) |
onApproved |
Customer is approved | Approval complete, applicationId is available |
Persist applicationId, then verify and reconcile on your server |
onDenied |
Customer is denied | Underwriting did not approve the application | Show a friendly message, offer other payment methods, log analytics |
onCanceled |
User closes or cancels | Flow ended without a decision | Return user to checkout, allow retry, do not submit order |
onNotification |
Informational status updates | Intermediate events (non-final) | Use for logging or lightweight UI hints, not final outcomes |
onError |
Validation, SDK, or network errors | Checkout could not complete | Show error UI, log details, surface validationErrors if present |
If you see session not initialized, ensure you call snap.init(token) before rendering or launching checkout, and use the correct token for the selected environment.
Step 7 — Get Approved treatment
The "Get Approved" flow uses snap.launch() with a marketing treatment payload. This is separate from checkout and is commonly used for promotional or pre-approval entry points.
If a specific SDK build exposes preApprovalButton and you must avoid it, install a guard after SDK load (shown below).
7.1 Get Approved — samples
7.1.1 Simple launch (promise)
async function launchGetApproved(payload){
if (!window.snap || typeof window.snap.launch !== 'function') {
throw new Error('snap.launch missing');
}
const res = window.snap.launch(payload);
return (res && typeof res.then === 'function') ? await res : res;
}
7.1.2 Full example with guard + storage + server verify
async function doGetApprovedFlow(){
const { access_token } = await fetch('/api/snap/token').then(r => r.json());
snap.init(access_token);
window.__SNAP_TOKEN__ = access_token;
// Guard unsupported API usage (optional)
if (window.snap && typeof window.snap.preApprovalButton !== 'function') {
window.snap.preApprovalButton = function(){ throw new Error('preApprovalButton blocked'); };
}
const payload = {
token: access_token,
marketing: { treatment: 'PRE_APPROVAL' },
data: { /* optional */ }
};
const result = await launchGetApproved(payload);
if (result && result.applicationId) {
sessionStorage.setItem('approvedApplicationId', result.applicationId);
await fetch('/api/snap/verify-approval', {
method:'POST',
headers:{ 'Content-Type':'application/json' },
body: JSON.stringify({ applicationId: result.applicationId })
});
}
}
If available, you may also call snap.sendMarketingMetadata(...) as a non-blocking step. Always catch errors and continue the flow.
Step 8 — Validate totals
function sumItems(arr){ return arr.reduce((s,i)=> s + Number(i.price)*Number(i.quantity), 0); }
function validateTransaction(tx){
const ci = tx && tx.cartInformation; if (!ci) throw new Error('cartInformation missing');
if (!ci.orderId) throw new Error('orderId is required');
const computed = sumItems(ci.items) + Number(ci.taxAmount) + Number(ci.shippingAmount) - Number(ci.discountAmount);
if (Math.abs(computed - Number(ci.totalAmount)) > 0.01) throw new Error('totalAmount mismatch');
}
Step 9 — Troubleshoot
| Symptom | Cause | Fix |
|---|---|---|
| Money fields rejected | Numbers used instead of strings | Send strings with two decimals, for example "12.34" |
| Quantity rejected | Quantity sent as string | Send integer |
| "Session not initialized" | Render or launch called before snap.init() | Initialize then render; add a single retry on init failures |
| Get Approved errors immediately | Missing token or unsupported SDK build | Verify token environment, confirm snap.launch exists, log Object.keys(window.snap) |
Step 10 — Security best practices
- Keep
client_secreton the server. Never embed it in frontend code. - Use HTTPS for production token requests and API calls.
- Cache tokens server-side and refresh before expiry.
- Verify approvals server-side using
applicationId.
Step 11 — Go-live checklist
- Server endpoint issues token and caches it.
- Frontend loads the correct SDK script for environment and calls
snap.init(token). - Transaction object validates locally before checkout.
- Checkout calls
actions.launchCheckout(transaction)and handles events. - Approvals are verified server-side using
applicationId.