Quick Start
Make your first payment with VINR in 5 minutes.
This guide walks you through making your first payment using the VINR API. By the end you'll have a working integration running against the sandbox — no real money, no KYB required.
This guide uses the hosted path: your server creates a payment, then redirects the customer to a VINR-hosted checkout page that handles the card form and 3DS. For embedded card fields or full API control, see Integration overview.
Get your API keys
Log in to your VINR Dashboard and navigate to Settings → API Keys. You'll find two keys per environment:
- Secret key (
sk_test_…) — server-side only. Used to create payments and verify webhooks. Never expose this to a browser. - Public key (
pk_test_…) — safe for client-side use. Initialises the embedded Elements UI (not needed for the hosted path).
Store the secret key in an environment variable — never commit it.
# .env
VINR_SECRET_KEY=sk_test_xxxxxxxxxxxxxxxxxxxxInstall the SDK
npm install @vinr/sdkRecommended project layout:
pip install vinrRequires Python 3.8+. The library is typed and works with any WSGI/ASGI framework (Flask, Django, FastAPI).
composer require vinr/vinr-phpRequires PHP 8.1+ and the ext-curl extension.
Create a payment
Create a pay_ object server-side and redirect your customer to the returned checkoutUrl. VINR collects the card details and handles 3DS on your behalf.
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({
secretKey: process.env.VINR_SECRET_KEY,
});
const payment = await vinr.payments.create({
amount: 1000, // Minor units — €10.00
currency: 'EUR',
description: 'Order #1234',
returnUrl: 'https://yoursite.com/payment/complete',
metadata: { orderId: '1234' },
});
// Redirect the customer
return Response.redirect(payment.checkoutUrl);import vinr
import os
client = vinr.Client(secret_key=os.environ["VINR_SECRET_KEY"])
payment = client.payments.create(
amount=1000, # Minor units — €10.00
currency="EUR",
description="Order #1234",
return_url="https://yoursite.com/payment/complete",
metadata={"order_id": "1234"},
)
# Redirect the customer
return redirect(payment.checkout_url)<?php
require_once 'vendor/autoload.php';
$vinr = new \Vinr\Client([
'secret_key' => getenv('VINR_SECRET_KEY'),
]);
$payment = $vinr->payments->create([
'amount' => 1000, // Minor units — €10.00
'currency' => 'EUR',
'description' => 'Order #1234',
'return_url' => 'https://yoursite.com/payment/complete',
'metadata' => ['order_id' => '1234'],
]);
// Redirect the customer
header('Location: ' . $payment->checkout_url);Handle the webhook
After the customer completes checkout, VINR delivers a payment.completed (or payment.failed) event to your webhook endpoint. This is the authoritative signal — do not fulfil orders based on the redirect alone.
import { Vinr } from '@vinr/sdk';
const vinr = new Vinr({ secretKey: process.env.VINR_SECRET_KEY });
export async function POST(req: Request) {
const payload = await req.text();
const signature = req.headers.get('x-vinr-signature');
const event = vinr.webhooks.verify(payload, signature);
switch (event.type) {
case 'payment.completed':
await fulfillOrder(event.data.metadata.orderId);
break;
case 'payment.failed':
await handleFailedPayment(event.data);
break;
}
return new Response('OK', { status: 200 });
}import vinr
import os
from flask import Flask, request, Response
client = vinr.Client(secret_key=os.environ["VINR_SECRET_KEY"])
app = Flask(__name__)
@app.route("/webhook", methods=["POST"])
def webhook():
payload = request.get_data(as_text=True)
signature = request.headers.get("X-Vinr-Signature")
event = client.webhooks.verify(payload, signature)
if event.type == "payment.completed":
fulfill_order(event.data.metadata["order_id"])
elif event.type == "payment.failed":
handle_failed_payment(event.data)
return Response("OK", status=200)<?php
require_once 'vendor/autoload.php';
$vinr = new \Vinr\Client([
'secret_key' => getenv('VINR_SECRET_KEY'),
]);
$payload = file_get_contents('php://input');
$signature = $_SERVER['HTTP_X_VINR_SIGNATURE'] ?? '';
$event = $vinr->webhooks->verify($payload, $signature);
switch ($event->type) {
case 'payment.completed':
fulfillOrder($event->data->metadata->order_id);
break;
case 'payment.failed':
handleFailedPayment($event->data);
break;
}
http_response_code(200);
echo 'OK';Always verify the x-vinr-signature header before trusting the payload. Reject any request where verification fails — return 400, not 200.
Test your integration
Use these test card numbers in the sandbox. Any future expiry date and any 3-digit CVV are accepted.
| Card number | Result |
|---|---|
4242 4242 4242 4242 | Successful payment |
4000 0000 0000 0002 | Declined |
4000 0000 0000 3220 | 3DS challenge required |
Trigger a test payment, confirm your webhook fires and returns 200, then check that your fulfilment logic ran. See Test mode & sandbox for the full set of test cards and simulated failure scenarios.
Next stepsAsk
Authentication
Scope, rotate, and secure your API keys.
Integration overview
Move from hosted to embedded or API-direct when you're ready.
Webhooks
Signature verification, retries, and idempotent event handling.
Payment methods
Cards, bank transfers, wallets — all supported methods.
Last updated on