Skip to main content

Installation

pip install drp-sdk --break-system-packages
Requirements:
  • Python 3.8 or higher
  • cryptography library (auto-installed)

Quick Start

Merchant Integration (Server-Side)

from drp_sdk import Client
import os

# Initialize client
client = Client(
    api_key=os.environ['DRP_API_KEY'],
    issuer_domain='issuer-bank.com',
    environment='production',  # or 'sandbox'
    private_key_path='./merchant-private-key.pem'
)

# Complete workflow: Get key, encrypt, send receipt
async def send_receipt(card_token, transaction_id, receipt_data):
    try:
        # 1. Get customer's public key
        key_data = client.public_keys.get(
            card_token,
            transaction_id=transaction_id
        )
        
        # 2. Encrypt and send receipt (SDK handles signing automatically)
        result = client.receipts.send({
            'receipt_id': f'rcpt_{int(time.time())}',
            'receipt': receipt_data,  # Dict with JSON-LD receipt
            'customer_public_key': key_data['public_key'],
            'customer_public_key_id': key_data['key_id'],
            'card_last_four': card_token[-4:],
            'transaction_id': transaction_id,
            'merchant_id': 'mch_coffee_shop_001',
            'amount': receipt_data['totalPaymentDue']['price'],
            'currency': receipt_data['totalPaymentDue']['priceCurrency']
        })
        
        print(f"Receipt sent: {result['delivery_confirmation']}")
        return result
        
    except CustomerNotEnrolledError:
        print('Customer not enrolled - skip Vero receipt')
        return None

Django Integration

# views.py
from django.http import JsonResponse
from drp_sdk import Client
from drp_sdk.exceptions import DRPError

drp_client = Client(
    api_key=settings.DRP_API_KEY,
    issuer_domain='issuer-bank.com',
    private_key_path=settings.DRP_PRIVATE_KEY_PATH
)

def checkout(request):
    card_token = request.POST.get('card_token')
    transaction_id = request.POST.get('transaction_id')
    
    # Process payment first...
    
    # Then send Vero receipt
    try:
        key_data = drp_client.public_keys.get(card_token)
        
        receipt = drp_client.receipts.send({
            'receipt_id': f'rcpt_{order.id}',
            'receipt': build_receipt_data(order),
            'customer_public_key': key_data['public_key'],
            'customer_public_key_id': key_data['key_id'],
            'card_last_four': card_token[-4:],
            'transaction_id': transaction_id,
            'merchant_id': settings.MERCHANT_ID,
            'amount': float(order.total),
            'currency': 'USD'
        })
        
        return JsonResponse({
            'success': True,
            'drp_confirmation': receipt['delivery_confirmation']
        })
        
    except DRPError as e:
        logger.error(f'Vero error: {e}')
        # Don't fail checkout if Vero fails
        return JsonResponse({'success': True, 'drp': False})

Flask Integration

# app.py
from flask import Flask, request, jsonify
from drp_sdk import Client

app = Flask(__name__)

drp_client = Client(
    api_key=app.config['DRP_API_KEY'],
    issuer_domain='issuer-bank.com',
    private_key_path='./keys/merchant-private.pem'
)

@app.route('/checkout', methods=['POST'])
def checkout():
    data = request.json
    
    try:
        # Get public key
        key_data = drp_client.public_keys.get(data['card_token'])
        
        # Send receipt
        result = drp_client.receipts.send({
            'receipt_id': f"rcpt_{data['order_id']}",
            'receipt': data['receipt'],
            'customer_public_key': key_data['public_key'],
            'customer_public_key_id': key_data['key_id'],
            'card_last_four': data['card_token'][-4:],
            'transaction_id': data['transaction_id'],
            'merchant_id': app.config['MERCHANT_ID'],
            'amount': data['amount'],
            'currency': 'USD'
        })
        
        return jsonify({'success': True, 'confirmation': result['delivery_confirmation']})
        
    except Exception as e:
        app.logger.error(f'Vero error: {e}')
        return jsonify({'success': True, 'drp': False})

FastAPI Integration

# main.py
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from drp_sdk import Client
from drp_sdk.exceptions import DRPError

app = FastAPI()

drp_client = Client(
    api_key=settings.DRP_API_KEY,
    issuer_domain='issuer-bank.com',
    private_key_path='./keys/merchant-private.pem'
)

class CheckoutRequest(BaseModel):
    card_token: str
    transaction_id: str
    receipt: dict
    amount: float

@app.post('/checkout')
async def checkout(request: CheckoutRequest):
    try:
        # Get public key
        key_data = await drp_client.public_keys.get_async(request.card_token)
        
        # Send receipt
        result = await drp_client.receipts.send_async({
            'receipt_id': f'rcpt_{int(time.time())}',
            'receipt': request.receipt,
            'customer_public_key': key_data['public_key'],
            'customer_public_key_id': key_data['key_id'],
            'card_last_four': request.card_token[-4:],
            'transaction_id': request.transaction_id,
            'merchant_id': settings.MERCHANT_ID,
            'amount': request.amount,
            'currency': 'USD'
        })
        
        return {'success': True, 'confirmation': result['delivery_confirmation']}
        
    except DRPError as e:
        # Log but don't fail checkout
        logger.error(f'Vero error: {e}')
        return {'success': True, 'drp': False}

Configuration

Client Options

from drp_sdk import Client

client = Client(
    # Required
    api_key='mch_live_abc123def456',
    issuer_domain='issuer-bank.com',
    
    # Optional
    environment='production',  # or 'sandbox'
    private_key_path='./merchant-private-key.pem',  # Path to key file
    private_key=None,  # Or provide key directly (PEM string)
    timeout=30.0,  # Request timeout in seconds
    max_retries=3,  # Max retry attempts
    log_level='INFO'  # DEBUG, INFO, WARNING, ERROR
)

Environment Variables

# .env
DRP_API_KEY=mch_live_abc123def456
DRP_ISSUER_DOMAIN=issuer-bank.com
DRP_ENVIRONMENT=production
DRP_PRIVATE_KEY_PATH=./merchant-private-key.pem
# Load with python-dotenv
from dotenv import load_dotenv
import os

load_dotenv()

client = Client(
    api_key=os.getenv('DRP_API_KEY'),
    issuer_domain=os.getenv('DRP_ISSUER_DOMAIN'),
    environment=os.getenv('DRP_ENVIRONMENT'),
    private_key_path=os.getenv('DRP_PRIVATE_KEY_PATH')
)

API Reference

Client Methods

public_keys.get(card_token, transaction_id=None)

Retrieve customer’s public key for encryption.
key_data = client.public_keys.get(
    'tok_card_abc123',
    transaction_id='txn_auth_456'
)

print(key_data['public_key'])  # PEM-formatted public key
print(key_data['key_id'])      # Key identifier
print(key_data['expires_at'])  # Expiration timestamp
Returns: dict with public key data

receipts.send(params)

Encrypt and send a receipt to the card issuer.
result = client.receipts.send({
    'receipt_id': 'rcpt_unique_123',
    'receipt': receipt_json_ld,
    'customer_public_key': key_data['public_key'],
    'customer_public_key_id': key_data['key_id'],
    'card_last_four': '1234',
    'transaction_id': 'txn_auth_456',
    'merchant_id': 'mch_coffee_shop_001',
    'amount': 12.50,
    'currency': 'USD'
})

print(result['delivery_confirmation'])
print(result['issuer_signature'])
Returns: dict with receipt response

receipts.list(filters) - User Client Only

Retrieve user’s receipts.
from drp_sdk import UserClient

user_client = UserClient(
    access_token=user_oauth_token,
    issuer_domain='your-bank.com'
)

response = user_client.receipts.list(
    start_date='2025-11-01',
    end_date='2025-12-07',
    merchant_id='mch_coffee_shop_001',
    limit=20,
    offset=0
)

print(response['receipts'])
print(response['pagination'])
Returns: dict with receipts list and pagination

merchants.register(data) - Class Method

Register merchant account (one-time setup).
from drp_sdk import Client

registration = Client.register(
    registration_key='reg_key_xyz789',
    issuer_domain='issuer-bank.com',
    merchant_id='mch_coffee_shop_001',
    merchant_name="Joe's Coffee Shop",
    contact_email='[email protected]',
    callback_url='https://coffeeshop.com/drp/webhook',
    public_key=public_key_pem,
    business_address={
        'street': '123 Main St',
        'city': 'Seattle',
        'state': 'WA',
        'postal_code': '98101',
        'country': 'US'
    }
)

print(registration['api_key'])        # Save securely!
print(registration['webhook_secret'])  # Save securely!
Returns: dict with registration response

Async Support (FastAPI, asyncio)

All methods have async equivalents:
# Async methods
key_data = await client.public_keys.get_async(card_token)
result = await client.receipts.send_async(params)
receipts = await user_client.receipts.list_async(filters)

Crypto Utilities

crypto.encrypt(data, public_key)

encrypted = client.crypto.encrypt(
    json.dumps(receipt_data),
    customer_public_key
)

crypto.decrypt(encrypted_data, private_key)

decrypted = client.crypto.decrypt(
    encrypted_base64,
    user_private_key
)
receipt_data = json.loads(decrypted)

crypto.sign(data, private_key)

signature = client.crypto.sign(
    json.dumps(receipt_data),
    merchant_private_key
)

crypto.verify(data, signature, public_key)

is_valid = client.crypto.verify(
    json.dumps(receipt_data),
    signature,
    merchant_public_key
)

Error Handling

from drp_sdk.exceptions import (
    DRPError,
    RateLimitError,
    AuthenticationError,
    CustomerNotEnrolledError,
    ValidationError
)

try:
    client.receipts.send(receipt_data)
except RateLimitError as e:
    print(f'Rate limited. Retry after {e.retry_after} seconds')
    time.sleep(e.retry_after)
    # Retry logic
except AuthenticationError:
    print('Invalid API key')
except CustomerNotEnrolledError:
    # Customer hasn't enabled Vero - skip
    pass
except ValidationError as e:
    print(f'Invalid data: {e.errors}')
except DRPError as e:
    print(f'Vero Error: {e}')
    raise

Exception Hierarchy

DRPError (base)
 AuthenticationError
 RateLimitError
 ValidationError
 NetworkError
 EncryptionError
 SignatureError

Testing

Mock Client

from drp_sdk.testing import MockClient

mock_client = MockClient()

# Mock responses
mock_client.public_keys.get.return_value = {
    'public_key': 'mock-public-key-pem',
    'key_id': 'pk_mock_123',
    'algorithm': 'RSA-2048-OAEP',
    'expires_at': '2026-12-07T00:00:00Z'
}

mock_client.receipts.send.return_value = {
    'status': 'accepted',
    'receipt_id': 'rcpt_123',
    'delivery_confirmation': 'conf_mock_456'
}

# Use in tests
result = mock_client.receipts.send(params)
assert result['status'] == 'accepted'

pytest Example

import pytest
from drp_sdk import Client
from drp_sdk.testing import MockClient

@pytest.fixture
def drp_client():
    return MockClient()

def test_send_receipt(drp_client):
    drp_client.receipts.send.return_value = {
        'status': 'accepted',
        'delivery_confirmation': 'conf_123'
    }
    
    result = drp_client.receipts.send({
        'receipt_id': 'test_123',
        # ... other params
    })
    
    assert result['status'] == 'accepted'
    drp_client.receipts.send.assert_called_once()

Webhooks

Handle webhook events from issuers:
from flask import Flask, request, jsonify
from drp_sdk import webhooks
import os

app = Flask(__name__)

@app.route('/drp/webhook', methods=['POST'])
def handle_webhook():
    signature = request.headers.get('X-DRP-Signature')
    webhook_secret = os.getenv('DRP_WEBHOOK_SECRET')
    
    # Verify webhook signature
    is_valid = webhooks.verify(
        request.get_data(),
        signature,
        webhook_secret
    )
    
    if not is_valid:
        return jsonify({'error': 'Invalid signature'}), 401
    
    # Handle event
    event_data = request.json
    event = event_data['event']
    receipt_id = event_data['receipt_id']
    
    if event == 'receipt.delivered':
        print(f'Receipt {receipt_id} delivered')
    elif event == 'receipt.disputed':
        details = event_data['details']
        print(f'Receipt {receipt_id} disputed: {details["dispute_reason"]}')
        # Handle dispute
    
    return jsonify({'received': True})

Best Practices

Never hardcode API keys. Use environment variables or secret management (AWS Secrets Manager, HashiCorp Vault).
Always catch CustomerNotEnrolledError - not all customers have Vero enabled. This is normal and shouldn’t fail the checkout.
FastAPI is async-native. Use get_async() and send_async() methods for better performance.
Public keys are valid for months. Cache them with Redis or in-memory cache to reduce API calls.
from functools import lru_cache

@lru_cache(maxsize=1000)
def get_cached_public_key(card_token):
    return client.public_keys.get(card_token)
Vero errors shouldn’t prevent checkout. Log the error, fall back to email receipts, and investigate later.

Resources


Changelog

v1.0.0 (Latest)

  • Initial stable release
  • Full support for Vero API v1
  • Async/await support
  • Django, Flask, FastAPI examples
  • Type hints throughout

v0.9.0 (Beta)

  • Beta release for testing
  • Core merchant and user APIs
See full changelog on GitHub.