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
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.
All API methods return strongly-typed Pydantic models using the nekuda SDK:
Copy
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)
Copy
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)
class CardRevealTokenResponse: reveal_token: str # One-time use
Copy
token reveal_path: str # API endpoint for reveal expires_at: datetime? # Tokenexpiration (optional) ```</Tab><Tab title="CardDetailsResponse">```pythonclass 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)
# 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
# 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")
Always wrap API calls in try-catch blocks. Refer to the Error Handling guide for details.
Copy
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
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.