Swipelux

End-User Custodial Wallet + On-Ramp Checkout

Provision custodial wallets per user and integrate with hosted or embedded checkout flows for USD→USDC conversions

Summary

Provision custodial wallets per user and let them buy USDC via card using a hosted Payment Link or embedded widget. Swipelux handles KYC and checkout; you receive definitive states via webhooks and verify via REST. Expect reduced KYC friction for returning users and faster time-to-first-USDC.

Problem → Solution mapping

Pain pointCapabilityMechanism
Need wallets per userCustomers + Wallet APIPOST /v1/customers then POST /v1/customers/{id}/wallets returns wallets[...]
KYC burdenKYC handledSubmit identifying documents; Swipelux returns status
Checkout complexityHosted/embeddedPayment Link or SDK modal with apiKey
State driftREST re-verifyWebhook + GET /v1/transfers/{id} before updating UI

Architecture

Implementation steps

Create customer (KYC)

curl https://wallet.swipelux.com/v1/customers \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sk_test_1234567890abcdef' \
  --data '{
    "firstName":"John","lastName":"Doe","email":"john.doe@example.com","phone":"+1234567890",
    "birthDate":"1990-01-01",
    "residentialAddress":{"streetLine1":"123 Main St","city":"San Francisco","state":"CA","postalCode":"94101","country":"US"},
    "identifyingInformation":[{"type":"drivers_license","issuingCountry":"US",
      "frontSideImage":"data:image/png;base64,AAA...","backSideImage":"data:image/png;base64,BBB..."}]
  }'

Provision wallet

curl https://wallet.swipelux.com/v1/customers/cus_cK69MttD5nAUAbud1B/wallets \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sk_test_1234567890abcdef' \
  --data '{ "chains": ["base"] }'

Create transfer (USD→USDC)

curl https://wallet.swipelux.com/v1/transfers \
  --request POST \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sk_test_1234567890abcdef' \
  --data '{
    "onBehalfOf":"cus_cK69MttD5nAUAbud1B",
    "amount":"50.0",
    "source":{"paymentRail":"card","currency":"USD"},
    "destination":{"currency":"USDC"}
  }'
<a id="buy" href="https://track.swipelux.com">Buy</a>
<script>
  const settings = { apiKey: "pk_test_abc123" };
  const url = `https://track.swipelux.com/?specificSettings=${encodeURIComponent(JSON.stringify(settings))}`;
  document.getElementById('buy').href = url;
</script>

Verify completion & show balance

curl https://wallet.swipelux.com/v1/transfers/tr_nEZHWur1pBSAKY7NlV \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sk_test_1234567890abcdef'
curl https://wallet.swipelux.com/v1/customers/cus_cK69MttD5nAUAbud1B/balances \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sk_test_1234567890abcdef'

Webhooks (subscribe, validate, verify)

curl https://wallet.swipelux.com/v1/webhooks \
  --request PATCH \
  --header 'Content-Type: application/json' \
  --header 'X-API-Key: sk_test_1234567890abcdef' \
  --data '{ "url": "https://your-domain.com/webhook" }'
// Node.js signature verification (HMAC-SHA256)
const crypto = require('crypto');
const secretKey = process.env.WEBHOOK_SECRET_KEY; // store from PATCH response
 
function verify(sig, rawBody) {
  const expected = crypto.createHmac('sha256', secretKey).update(rawBody).digest('hex');
  return crypto.timingSafeEqual(Buffer.from(sig, 'hex'), Buffer.from(expected, 'hex'));
}

Always re-fetch state:

# After receiving transfer.completed webhook:
curl https://wallet.swipelux.com/v1/transfers/tr_nEZHWur1pBSAKY7NlV \
  --header 'X-API-Key: sk_test_1234567890abcdef'

UX choices

Hosted link = fastest; embed the widget for best continuity. Apple Pay is available where supported by device and issuing bank.

KPIs

transfer.completed rate; time-to-first-USDC; KYC start→finish drop-off.

Limits & caveats

Fiat: USD only. Crypto: USDC only (today). Treat REST as source of truth; don't act on webhook alone.

Troubleshooting

Common state: awaiting_funds, completed, failed. Retry idempotently on HTTP 5xx. If signature mismatch, return 401 and drop the event; do a periodic REST reconciliation job.