Quick Start

Make your first payment with VINR in 5 minutes.

View as MarkdownInstall skills

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_xxxxxxxxxxxxxxxxxxxx

Install the SDK

npm install @vinr/sdk

Recommended project layout:

app.ts
payments.ts
webhooks.ts
package.json
pip install vinr

Requires Python 3.8+. The library is typed and works with any WSGI/ASGI framework (Flask, Django, FastAPI).

composer require vinr/vinr-php

Requires 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 numberResult
4242 4242 4242 4242Successful payment
4000 0000 0000 0002Declined
4000 0000 0000 32203DS 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

Was this page helpful?
Edit on GitHub

Last updated on

On this page