Integrating Stablecoin Payments with the Plirin API: A Developer's Guide
If you’re building a business that wants to accept crypto payments, you probably don’t want to deal with a black-box payment processor that treats stablecoins like an afterthought. You need an API you can actually trust, one built for the reality of how businesses move money today. That’s where Plirin’s REST API, webhooks, and SDKs come in — and this guide walks you through everything, from your first sandbox test to collecting real payments in production.
Let’s get practical. You’ll learn how to build a payment flow, handle webhooks that trigger when customers pay, and deploy with confidence.
Why Integrate via API Instead of Payment Links?
First, a quick reality check. Plirin’s payment links are dead simple — create a link, share it, get paid. No code required. But if you’re running a SaaS, e-commerce platform, or any app where payment is core to your product, you need deeper control. You need to:
- Create charges programmatically when a customer checks out
- Store payment states in your own database
- Trigger custom workflows when payments land (send an order confirmation, provision an account, run a background job)
- Build a branded checkout experience that feels like part of your app
That’s what the API gives you. It’s the difference between slapping a link on your site and building real crypto commerce infrastructure.
Understanding the Plirin Payment Flow
Before you write a line of code, understand how money moves:
- You create a charge via the API with an amount, description, and optional customer metadata
- We generate a payment address on the blockchain you specify (Solana or EVM chains)
- The customer sends USDC or USDT to that address
- We detect the payment and fire a webhook to your server
- Your app reacts — mark the order as paid, unlock the feature, send a receipt
The beauty here is that funds flow directly to your wallet. No middleman holding your money. No settlement delays. Just transparent, instant settlement on-chain.
Getting Started: Authentication and Credentials
Head to your Plirin dashboard and navigate to Settings > API Keys. You’ll see two things:
- Public Key — safe to embed in frontend code, used to initialize payment flows
- Secret Key — never share this. Use it server-side to create charges and verify webhooks
Keep your secret key in an environment variable. Never commit it to git. If you accidentally expose it, rotate it immediately from the dashboard.
PLIRIN_SECRET_KEY=sk_live_your_actual_key_here
PLIRIN_PUBLIC_KEY=pk_live_your_public_key_here
Using the REST API: Creating Your First Charge
The Plirin API follows REST conventions. All requests go to https://api.plirin.com/v1/ and use standard HTTP methods. Let’s create a charge.
Basic Request
curl -X POST https://api.plirin.com/v1/charges \
-H "Authorization: Bearer sk_live_your_key" \
-H "Content-Type: application/json" \
-d '{
"amount": 2500,
"currency": "usd",
"description": "Premium plan upgrade",
"blockchain": "solana",
"customer_id": "cust_user123"
}'
The response includes:
{
"id": "ch_abc123xyz",
"amount": 2500,
"currency": "usd",
"status": "pending",
"payment_address": "EPjFWdd5Au17h7xNceVBj4AxC23yUvoMRRy8uK2EyUU",
"blockchain": "solana",
"expires_at": "2025-01-15T14:32:00Z",
"created_at": "2025-01-15T14:22:00Z"
}
That payment_address is where your customer sends money. You can display it as a QR code, a text field, or pass it to a wallet connector like Magic or Phantom.
SDK Support: TypeScript and Python
Writing raw curl requests gets old. Plirin provides official SDKs that handle auth, parsing, and error handling for you.
TypeScript/JavaScript
import { Plirin } from "@plirin/sdk";
const client = new Plirin({
secretKey: process.env.PLIRIN_SECRET_KEY,
});
async function createCharge(userId: string, amountUsd: number) {
const charge = await client.charges.create({
amount: Math.round(amountUsd * 100), // Convert to cents
currency: "usd",
description: `Payment for user ${userId}`,
blockchain: "solana",
customer_id: userId,
});
return charge;
}
// Usage
const charge = await createCharge("user_456", 25.00);
console.log(`Pay to: ${charge.payment_address}`);
Python
from plirin import Plirin
client = Plirin(secret_key=os.getenv("PLIRIN_SECRET_KEY"))
def create_charge(user_id: str, amount_usd: float):
charge = client.charges.create(
amount=int(amount_usd * 100), # Convert to cents
currency="usd",
description=f"Payment for user {user_id}",
blockchain="solana",
customer_id=user_id
)
return charge
charge = create_charge("user_456", 25.00)
print(f"Pay to: {charge.payment_address}")
Both SDKs handle request signing, retries on transient failures, and give you type hints. If you’re using FastAPI, Django, Express, or Next.js, the SDK integrates cleanly.
Webhooks: Reacting When Customers Pay
This is where your app comes alive. Instead of polling Plirin asking “Is it paid yet?”, we tell you when a payment lands. Webhooks are HTTP POST requests from our servers to an endpoint you control.
Setting Up Your Webhook Endpoint
Create an endpoint that accepts JSON and verifies the signature:
import express from "express";
import crypto from "crypto";
const app = express();
app.use(express.json());
const WEBHOOK_SECRET = process.env.PLIRIN_WEBHOOK_SECRET;
app.post("/webhooks/plirin", (req, res) => {
// Verify the signature
const signature = req.headers["x-plirin-signature"] as string;
const body = JSON.stringify(req.body);
const hash = crypto
.createHmac("sha256", WEBHOOK_SECRET)
.update(body)
.digest("hex");
if (hash !== signature) {
return res.status(401).json({ error: "Invalid signature" });
}
// Process the event
const event = req.body;
if (event.type === "charge.paid") {
const charge = event.data;
console.log(`Charge ${charge.id} paid for customer ${charge.customer_id}`);
// Update your database
db.payments.update(
{ charge_id: charge.id },
{ status: "paid", paid_at: new Date() }
);
// Trigger your workflow
sendOrderConfirmation(charge.customer_id);
provisioning(charge.customer_id);
}
res.json({ success: true });
});
Important: Always verify the signature. It proves the webhook came from Plirin, not some attacker spoofing a request.
Registering the Webhook
Go to Settings > Webhooks in your dashboard and add your endpoint URL. You can test it directly from the dashboard — Plirin will send a sample event and show you the response.
Sandbox vs. Mainnet: Testing Before You Go Live
Never test real transactions on mainnet. Use our sandbox environment first.
Sandbox Setup
- Create a separate Plirin account (or use a sandbox project)
- Use sandbox API keys from Settings > API Keys > Sandbox
- Requests go to
https://sandbox-api.plirin.com/v1/ - Fund a test wallet with testnet tokens (Solana devnet or Ethereum sepolia, depending on your chain)
In sandbox, charges never expire, and you can manually mark them as paid from the dashboard to test your webhook handling.
Moving to Mainnet
Once you’ve tested:
- Switch to mainnet API keys
- Update your endpoint from
sandbox-api.plirin.comtoapi.plirin.com - Make sure your webhook secret is set
- Run a small test transaction with real funds (or ask Plirin for a test credit)
- Monitor your logs for the webhook
Error Handling and Retries
Stuff breaks. Networks hiccup. Your server goes down for 30 seconds. Build for it.
API Errors
try {
const charge = await client.charges.create({
amount: 2500,
currency: "usd",
blockchain: "solana",
});
} catch (error) {
if (error.code === "network_error") {
console.error("Network failed, retry in 5 seconds");
// Implement exponential backoff
} else if (error.code === "rate_limit") {
console.error("Hit rate limit, back off");
} else {
console.error("API error:", error.message);
}
}
Webhook Retries
Plirin retries failed webhooks (you return a non-2xx status) up to 5 times over 24 hours. Always be idempotent — if you receive the same webhook twice, processing it twice shouldn’t break anything.
// Good: Idempotent
db.payments.update(
{ charge_id: charge.id },
{ status: "paid", paid_at: new Date() },
{ upsert: true }
);
// Bad: Not idempotent
db.payments.create({ charge_id: charge.id, status: "paid" });
// Second webhook triggers duplicate!
Choosing Your Blockchain: Solana or EVM?
When you create a charge, specify blockchain: "solana" or blockchain: "ethereum" (or Base, Polygon, etc. for EVM chains). This determines which network the payment address lives on.
Solana — faster confirmations, lower fees, simpler integration. If your customers have Phantom or Marinade wallets, they’re ready to go.
Ethereum/Base/Polygon — more wallet options (MetaMask, Coinbase Wallet, etc.), wider adoption. Slightly higher fees and confirmation times.
For detailed chain comparisons, check out our guide on choosing the right blockchain for your payments.
Building a Complete Checkout Flow
Let’s tie it together. Here’s a real example: a SaaS charging for plan upgrades.
Frontend:
async function handleCheckout(planId: string, amount: number) {
const response = await fetch("/api/checkout", {
method: "POST",
body: JSON.stringify({ plan_id: planId, amount }),
});
const { payment_address, charge_id } = await response.json();
// Show a modal with QR code + address
showPaymentModal({
address: payment_address,
amount,
onConfirm: async () => {
// Poll or wait for webhook
const result = await waitForPayment(charge_id);
if (result.status === "paid") {
// Redirect to dashboard
window.location.href = "/dashboard";
}
},
});
}
Backend (Node.js):
app.post("/api/checkout", async (req, res) => {
const { plan_id, amount } = req.body;
const userId = req.user.id;
// Create a charge
const charge = await client.charges.create({
amount: Math.round(amount * 100),
currency: "usd",
description: `Plan upgrade: ${plan_id}`,
blockchain: "solana",
customer_id: userId,
});
// Store it in your DB
await db.orders.insert({
user_id: userId,
charge_id: charge.id,
plan_id,
status: "pending",
created_at: new Date(),
});
res.json({
payment_address: charge.payment_address,
charge_id: charge.id,
});
});
// Webhook handler (from earlier)
app.post("/webhooks/plirin", async (req, res) => {
const event = req.body;
if (event.type === "charge.paid") {
const order = await db.orders.findOne({
charge_id: event.data.id,
});
if (order) {
await db.orders.update(
{ charge_id: event.data.id },
{ status: "paid", paid_at: new Date() }
);
// Upgrade the user's plan
await db.users.update(
{ id: order.user_id },
{ plan: order.plan_id, upgrade_date: new Date() }
);
}
}
res.json({ success: true });
});
This is a real, working pattern. Customer clicks checkout → you create a charge → they pay → webhook tells you → you unlock the feature.
Advanced: Handling Refunds and Disputes
Sometimes you need to refund a customer. Plirin doesn’t automatically reverse on-chain transactions (that’s not how blockchain works), but you can handle refunds in your app:
- Check if the customer’s charge has a
refund_addressset - Send USDC or USDT directly to that address from your wallet
- Mark the order as refunded in your database
For a deeper dive, read our guide on stablecoin refund management.
Monitoring, Logging, and Debugging
In production, you need visibility:
- Log all API calls: timestamps, amounts, customer IDs, response codes
- Monitor webhook delivery: track which webhooks succeed vs. fail
- Set alerts: if webhooks fail for more than 5 minutes, page on-call
- Track charge creation to payment: measure how long customers take to pay
Use Plirin’s dashboard to view all charges and events. Export transaction data for accounting via the API or CSV export.
Security Checklist Before Launch
- Store API keys in environment variables, never in code
- Verify webhook signatures on every request
- Use HTTPS for your webhook endpoint
- Implement rate limiting on your API routes
- Test error handling (network failures, timeouts)
- Audit database queries for SQL injection
- Use a dedicated business wallet, never a personal one
- Rotate API keys regularly
- Review Plirin’s crypto payment compliance guide for KYB/AML requirements
Next Steps: From Integration to Scale
Once you’ve integrated the API and taken your first live payment, you’re ready to optimize. Consider:
- Recurring billing: for subscriptions, use recurring billing with stablecoins
- Multi-chain strategy: accept payments on both Solana and Ethereum to reach more customers
- Webhook automation: trigger internal systems (inventory, fulfillment, analytics)
- Analytics: track which chains, amounts, and customer types drive revenue
The API gives you the foundation. Your product, and how you use it, is what matters.
Ready to accept stablecoin payments via API? Create a free account and start building in sandbox today. Check out Plirin’s pricing to see which tier fits your business, and when you’re ready, head to the waitlist to unlock mainnet access.