The nekuda payment flow for backend processing consists of these main steps:
1
Initialize Client
Set up the NekudaClient with your API key.
2
Create User Context
Obtain a UserContext by providing a unique user_id for your end-user.
3
Create Mandate
Describe the purchase intent by creating a MandateData object. This action
returns a mandate_id.
4
Request Reveal Token
Use the mandate_id to get a short-lived, single-use token to access card
details.
5
Reveal Card Details
Exchange the token for actual card information to complete the purchase
(e.g., with a payment processor).
Important Workflow: A user_id is required to create a user context.
A MandateData object must be created and submitted to the API via
user.create_mandate() to obtain a mandate_id. This mandate_id is then
required to request a reveal_token.
Here’s a full working example demonstrating the complete payment flow with the nekuda SDK:
payment_flow.py
Copy
Ask AI
from nekuda import MandateData, NekudaClient, NekudaErrorimport os# Initialize client - uses NEKUDA_API_KEY from environment by default.# Default base_url is https://api.nekuda.aiclient = NekudaClient.from_env()# Or with explicit config (not needed if NEKUDA_API_KEY is set):# client = NekudaClient(# api_key="sk_live_your_api_key",# # base_url="https://api.nekuda.ai", # Default, no need to specify for production# timeout=30,# max_retries=3# )# 1️⃣ Provide a unique user_id for your end-user.user_id = "user_integration_test_123"user = client.user(user_id)try: # 2️⃣ Create a mandate (describes the purchase intent) mandate = MandateData( product="Premium Gold Subscription", price=99.99, currency="USD", merchant="nekuda Demo Store", merchant_link="https://app.nekuda.ai/demostore", product_description="Monthly access to all gold features" ) # The user_id (user_integration_test_123) is implicitly passed. mandate_response = user.create_mandate(mandate) # Type-safe access to all fields! print(f"✅ Mandate Created: {mandate_response.mandate_id} for user: {user_id}") print(f" Request ID: {mandate_response.request_id}") print(f" Created At: {mandate_response.created_at}") # 3️⃣ Request reveal token using the mandate_id from the previous step. reveal_response = user.request_card_reveal_token( mandate_id=mandate_response.mandate_id # Can use int or string ) print(f"🔑 Reveal Token Generated: {reveal_response.reveal_token[:20]}...") print(f" API Path for Reveal: {reveal_response.reveal_path}") # Check expiration if available if reveal_response.expires_at: print(f" Token expires at: {reveal_response.expires_at}") # 4️⃣ Reveal card details using the single-use reveal_token. # The user_id is also implicitly passed here. card = user.reveal_card_details(reveal_response.reveal_token) # All fields are typed and validated! print(f"💳 Card Revealed: **** **** **** {card.card_number[-4:]}") print(f" Expiry: {card.card_expiry_date}") # Guaranteed MM/YY format print(f" Name: {card.cardholder_name}") # Optional fields are properly typed if card.email: print(f" Email: {card.email}") if card.billing_address: print(f" Address: {card.billing_address}") print("🎉 Successfully completed the card reveal flow!")except NekudaError as e: # Structured error handling print(f"❌ An error occurred: {e}") if hasattr(e, 'status_code'): print(f" HTTP Status: {e.status_code}") if hasattr(e, 'code'): print(f" Error Code: {e.code}") # For more details on error handling, see the Errors guide.
payment_flow.py
Copy
Ask AI
from nekuda import MandateData, NekudaClient, NekudaErrorimport os# Initialize client - uses NEKUDA_API_KEY from environment by default.# Default base_url is https://api.nekuda.aiclient = NekudaClient.from_env()# Or with explicit config (not needed if NEKUDA_API_KEY is set):# client = NekudaClient(# api_key="sk_live_your_api_key",# # base_url="https://api.nekuda.ai", # Default, no need to specify for production# timeout=30,# max_retries=3# )# 1️⃣ Provide a unique user_id for your end-user.user_id = "user_integration_test_123"user = client.user(user_id)try: # 2️⃣ Create a mandate (describes the purchase intent) mandate = MandateData( product="Premium Gold Subscription", price=99.99, currency="USD", merchant="nekuda Demo Store", merchant_link="https://app.nekuda.ai/demostore", product_description="Monthly access to all gold features" ) # The user_id (user_integration_test_123) is implicitly passed. mandate_response = user.create_mandate(mandate) # Type-safe access to all fields! print(f"✅ Mandate Created: {mandate_response.mandate_id} for user: {user_id}") print(f" Request ID: {mandate_response.request_id}") print(f" Created At: {mandate_response.created_at}") # 3️⃣ Request reveal token using the mandate_id from the previous step. reveal_response = user.request_card_reveal_token( mandate_id=mandate_response.mandate_id # Can use int or string ) print(f"🔑 Reveal Token Generated: {reveal_response.reveal_token[:20]}...") print(f" API Path for Reveal: {reveal_response.reveal_path}") # Check expiration if available if reveal_response.expires_at: print(f" Token expires at: {reveal_response.expires_at}") # 4️⃣ Reveal card details using the single-use reveal_token. # The user_id is also implicitly passed here. card = user.reveal_card_details(reveal_response.reveal_token) # All fields are typed and validated! print(f"💳 Card Revealed: **** **** **** {card.card_number[-4:]}") print(f" Expiry: {card.card_expiry_date}") # Guaranteed MM/YY format print(f" Name: {card.cardholder_name}") # Optional fields are properly typed if card.email: print(f" Email: {card.email}") if card.billing_address: print(f" Address: {card.billing_address}") print("🎉 Successfully completed the card reveal flow!")except NekudaError as e: # Structured error handling print(f"❌ An error occurred: {e}") if hasattr(e, 'status_code'): print(f" HTTP Status: {e.status_code}") if hasattr(e, 'code'): print(f" Error Code: {e.code}") # For more details on error handling, see the Errors guide.
payment_flow.ts
Copy
Ask AI
import { NekudaClient, MandateData, NekudaError } fromt '@nekuda/nekuda-js';async function paymentFlow() { // Initialize client with automatic URL normalization const client = NekudaClient.fromEnv(); // Uses NEKUDA_API_KEY env var // Or with explicit config: // const client = new NekudaClient('sk_live_...', { // baseUrl: 'api.nekuda.ai', // No need for https:// prefix! // timeout: 30000, // maxRetries: 5 // }); const user = client.user('user_integration_test_123'); try { // 1️⃣ Create a mandate (returns MandateCreateResponse) const mandate = new MandateData({ product: 'Premium Gold Subscription', price: 99.99, currency: 'USD', merchant: 'Nekuda Demo Store', merchantLink: 'https://app.nekuda.ai/demostore', productDescription: 'Monthly access to all gold features' }); const mandateResponse = await user.createMandate(mandate); // Type-safe access to all fields! console.log(`✅ Mandate Created: ${mandateResponse.mandateId} for user: user_integration_test_123`); console.log(` Request ID: ${mandateResponse.requestId}`); console.log(` Created at: ${mandateResponse.createdAt}`); // 2️⃣ Request reveal token (returns CardRevealTokenResponse) const revealResponse = await user.requestCardRevealToken( mandateResponse.mandateId // Can use number or string ); console.log(`🔑 Reveal Token Generated: ${revealResponse.revealToken.slice(0, 20)}...`); // Check expiration if available if (revealResponse.expiresAt) { console.log(` Token expires at: ${revealResponse.expiresAt}`); } // 3️⃣ Reveal card details (returns CardDetailsResponse) const card = await user.revealCardDetails(revealResponse.revealToken); // All fields are typed and validated! console.log(`💳 Card Revealed: **** **** **** ${card.cardNumber.slice(-4)}`); console.log(` Expiry: ${card.cardExpiryDate}`); // Guaranteed MM/YY format console.log(` Name: ${card.cardholderName}`); // Optional fields are properly typed if (card.email) { console.log(` Email: ${card.email}`); } if (card.billingAddress) { console.log(` Address: ${card.billingAddress}`); } console.log('🎉 Successfully completed the card reveal flow!'); } catch (error) { if (error instanceof NekudaError) { // Structured error handling console.log(`❌ An error occurred: ${error.message}`); if ('statusCode' in error) { console.log(` HTTP Status: ${(error as any).statusCode}`); } if ('code' in error) { console.log(` Error Code: ${(error as any).code}`); } } else { console.log(`❌ Unexpected error: ${error}`); } }}paymentFlow().catch(console.error);
All API methods return strongly-typed response models:
MandateCreateResponse
Copy
Ask AI
class MandateCreateResponse: mandate_id: int # Unique mandate identifier request_id: str # Idempotency key customer_id: str # This is the user_id you provided created_at: datetime # Creation timestamp updated_at: datetime? # Last update (optional)
CardRevealTokenResponse
Copy
Ask AI
class CardRevealTokenResponse: reveal_token: str # One-time use token reveal_path: str # API endpoint for reveal expires_at: datetime? # Token expiration (optional)
CardDetailsResponse
Copy
Ask AI
class CardDetailsResponse: card_number: str # Full card number (validated) card_expiry_date: str # MM/YY format (validated) cardholder_name: str # Name on card last4_digits: str? # Last 4 digits (optional) email: str? # Associated email (optional) billing_address: str? # Billing address (optional) zip_code: str? # ZIP code (optional) phone_number: str? # Phone number (optional)
MandateCreateResponse
Copy
Ask AI
class MandateCreateResponse: mandate_id: int # Unique mandate identifier request_id: str # Idempotency key customer_id: str # This is the user_id you provided created_at: datetime # Creation timestamp updated_at: datetime? # Last update (optional)
CardRevealTokenResponse
Copy
Ask AI
class CardRevealTokenResponse: reveal_token: str # One-time use token reveal_path: str # API endpoint for reveal expires_at: datetime? # Token expiration (optional)
CardDetailsResponse
Copy
Ask AI
class CardDetailsResponse: card_number: str # Full card number (validated) card_expiry_date: str # MM/YY format (validated) cardholder_name: str # Name on card last4_digits: str? # Last 4 digits (optional) email: str? # Associated email (optional) billing_address: str? # Billing address (optional) zip_code: str? # ZIP code (optional) phone_number: str? # Phone number (optional)
from nekuda import NekudaClientclient = NekudaClient.from_env()user = client.user("some_user_id")# Assume mandate_id is obtainedreveal_response = user.request_card_reveal_token(mandate_id=123)reveal_response. # IDE shows: reveal_token, reveal_path, expires_at
Copy
Ask AI
from nekuda import NekudaClientclient = NekudaClient.from_env()user = client.user("some_user_id")# Assume mandate_id is obtainedreveal_response = user.request_card_reveal_token(mandate_id=123)reveal_response. # IDE shows: reveal_token, reveal_path, expires_at
Copy
Ask AI
import { NekudaClient } fromt '@nekuda/nekuda-js';const client = NekudaClient.fromEnv();const user = client.user('some_user_id');// Your IDE knows all available fieldsconst revealResponse = await user.requestCardRevealToken(123);revealResponse. // IDE shows: revealToken, expiresAt
# Card details are automatically validated by the nekuda SDK# card = user.reveal_card_details(reveal_token="...")# card.card_expiry_date is guaranteed to be in MM/YY format# card.card_number is validated to be 13-19 digits
Copy
Ask AI
# Card details are automatically validated by the nekuda SDK# card = user.reveal_card_details(reveal_token="...")# card.card_expiry_date is guaranteed to be in MM/YY format# card.card_number is validated to be 13-19 digits
Copy
Ask AI
// Card details are automatically validated// const card = await user.revealCardDetails(token);// card.cardExpiryDate is guaranteed to be in MM/YY format// card.cardNumber is validated to be 13-19 digits
# Both work - SDK converts to string internally if needed# reveal_response = user.request_card_reveal_token(mandate_id=123)# reveal_response = user.request_card_reveal_token(mandate_id="123")
Copy
Ask AI
# Both work - SDK converts to string internally if needed# reveal_response = user.request_card_reveal_token(mandate_id=123)# reveal_response = user.request_card_reveal_token(mandate_id="123")
Copy
Ask AI
// Both work - SDK converts to string internally// const revealResponse = await user.requestCardRevealToken(123);// const revealResponse = await user.requestCardRevealToken('123');
Always wrap API calls in try-catch blocks. Refer to the Error Handling guide for details.
Copy
Ask AI
from nekuda import NekudaError# try:# card = user.reveal_card_details(token)# # Process payment with card details# except NekudaError as e:# # Log error and handle gracefully# logger.error(f"Payment failed: {e}")# # Return error response to user
Copy
Ask AI
from nekuda import NekudaError# try:# card = user.reveal_card_details(token)# # Process payment with card details# except NekudaError as e:# # Log error and handle gracefully# logger.error(f"Payment failed: {e}")# # Return error response to user
Copy
Ask AI
import { NekudaError } fromt '@nekuda/nekuda-js';// try {// const card = await user.revealCardDetails(token);// // Process payment with card details// } catch (error) {// if (error instanceof NekudaError) {// // Log error and handle gracefully// console.error(`Payment failed: ${error.message}`);// // Return error response to user// }// }
The SDK automatically handles rate limiting with exponential backoff based on max_retries. For very high-volume applications, monitor API usage and consider if additional client-side rate limiting logic is beneficial.