@yodlpay/yapp-sdk v1.3.3
🚀 Yodl Yapp SDK
The official SDK for building Yodl Yapps. This SDK provides a secure way to interact with the Yodl platform, handling authentication, payment requests, and cross-origin communication.
📦 Installation
npm install @yodlpay/yapp-sdk
⚡ Quick Start
import YappSDK from '@yodlpay/yapp-sdk';
// Initialize the SDK with your domain and public key
const sdk = new YappSDK({
ensName: 'my-yapp.eth',
// origin: "https://yodl.me", // Optional: defaults to https://yodl.me
// publicKey: "my-test-public-key" // Optional: ES256 PEM encoded public key
});
💰 Payment Creation Example
Here's a focused example demonstrating how to create payments with the YappSDK:
import React, { useState, useEffect } from 'react';
import YappSDK, {
FiatCurrency,
PaymentResponse,
isInIframe,
} from '@yodlpay/yapp-sdk';
const sdk = new YappSDK({
ensName: 'my-yapp.eth',
});
function PaymentExample() {
const [paymentResult, setPaymentResult] = useState<PaymentResponse | null>(
null,
);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
// Check for payment information in URL on component mount
useEffect(() => {
// Parse payment information from URL (for redirect flow)
const urlPaymentResult = sdk.parsePaymentFromUrl();
if (urlPaymentResult) {
// Payment was successful via redirect
setPaymentResult(urlPaymentResult);
console.log('Payment successful (redirect):', urlPaymentResult);
// Clean the URL to prevent duplicate processing on refresh
// Note: You would need to implement this method or use history API
cleanPaymentUrl();
}
}, []);
// Helper function to clean payment parameters from URL
const cleanPaymentUrl = () => {
// Remove payment parameters from URL without page refresh
const url = new URL(window.location.href);
url.searchParams.delete('txHash');
url.searchParams.delete('chainId');
window.history.replaceState({}, document.title, url.toString());
};
// Create a new payment
const createPayment = async () => {
setIsLoading(true);
setError(null);
try {
// Recipient address - replace with your actual recipient
const recipientAddress = '0x742d35Cc6634C0532925a3b844Bc454e4438f44e';
// Create a unique memo/order ID
const orderId = `order_${Date.now()}`;
// Request payment
const response = await sdk.requestPayment(recipientAddress, {
amount: 50,
currency: FiatCurrency.USD,
memo: orderId,
redirectUrl: window.location.href, // Required for non-iframe mode
});
// Handle successful payment
setPaymentResult(response);
console.log('Payment successful:', response);
} catch (error) {
// Handle payment errors
console.error('Payment failed:', error);
if (error.message === 'Payment was cancelled') {
setError('Payment was cancelled by user');
} else if (error.message === 'Payment request timed out') {
setError('Payment request timed out after 5 minutes');
} else {
setError(`Payment failed: ${error.message}`);
}
} finally {
setIsLoading(false);
}
};
return (
<div className="payment-container">
<h1>Payment Example</h1>
{/* Payment result */}
{paymentResult && (
<div className="success-message">
<h2>Payment Successful!</h2>
<p>
<strong>Transaction Hash:</strong> {paymentResult.txHash}
</p>
<p>
<strong>Chain ID:</strong> {paymentResult.chainId}
</p>
</div>
)}
{/* Error message */}
{error && (
<div className="error-message">
<p>{error}</p>
</div>
)}
{/* Loading indicator */}
{isLoading && (
<div className="loading">
<p>Processing...</p>
</div>
)}
{/* Action buttons */}
<div className="button-group">
<button
onClick={createPayment}
disabled={isLoading}
className="payment-button"
>
Create New Payment
</button>
</div>
</div>
);
}
export default PaymentExample;
Key Points About Payment Creation
Singleton SDK Instance
- The SDK is initialized once outside the component as a singleton
- This allows the same SDK instance to be reused across multiple components
Creating a Payment
- The
createPayment
function demonstrates how to request a new payment - It includes proper error handling for common scenarios (cancellation, timeout)
- A unique memo/order ID is generated for each payment
- The
Payment States
- The example tracks loading states, errors, and successful payments
- It provides appropriate UI feedback for each state
Redirect Flow Support
- The component checks for payment information in the URL on mount using
parsePaymentFromUrl()
- Successfully handles both iframe and redirect payment flows
- Cleans up URL parameters after processing to prevent duplicate handling
- The component checks for payment information in the URL on mount using
🔄 Payment Flow
The SDK provides a streamlined payment flow:
// Make a payment request
const response = await sdk.requestPayment(address, config);
The payment flow handles both iframe and redirect modes automatically based on the environment.
Payment Configuration
amount
: Payment amount (positive number)currency
: Currency code fromFiatCurrency
enummemo
: Optional identifier/description (max 32 bytes)redirectUrl
: Required when not in iframe mode
Memo Field Usage
The memo
field serves two purposes:
- 📝 Human-readable payment description
- 🔍 Unique identifier for payment tracking
Example use cases:
const examples = [
{ amount: 50, currency: FiatCurrency.USD, memo: 'subscription_id_456' },
{ amount: 75, currency: FiatCurrency.EUR, memo: 'invoice_789' },
{ amount: 120, currency: FiatCurrency.THB, memo: 'product_xyz_123' },
];
🔍 Fetching Payment Details
Once you have the transaction hash from a successful payment, you can fetch the complete payment details using the Yodl API:
// Example of fetching payment details with the transaction hash
const fetchPaymentDetails = async (txHash) => {
try {
const response = await fetch(
`https://tx.yodl.me/api/v1/payments/${txHash}`,
);
const data = await response.json();
return data.payment;
} catch (error) {
console.error('Error fetching payment details:', error);
throw error;
}
};
The API response includes comprehensive payment information:
{
"payment": {
"chainId": 8453,
"txHash": "0x123c86bcf2a0aeadd269f30719a6ce7eef515a1a36600751a42ca77d42c802bc",
"paymentIndex": 0,
"destinationChainId": null,
"destinationTxHash": null,
"blockTimestamp": "2025-02-24T12:09:37.000Z",
"tokenOutSymbol": "USDC",
"tokenOutAddress": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
"tokenOutAmountGross": "10.03888",
"receiverAddress": "0xa1833B1A4DC461D3C025DbC99B71b127AEdbA45c",
"receiverEnsPrimaryName": null,
"invoiceCurrency": "USD",
"invoiceAmount": "10.04",
"senderAddress": "0x065c1BC23aE559BFFDBE5bbc335C30f30bE2b992",
"senderEnsPrimaryName": "maradona.yodl.eth"
}
}
This detailed information can be used for:
- Verifying payment amounts and currencies
- Recording sender and receiver information
- Tracking payment timestamps
- Implementing receipt generation
🖼️ Iframe Integration
Detection
import { isInIframe } from '@yodlpay/yapp-sdk';
if (isInIframe()) {
console.log('Running inside an iframe');
// Implement iframe-specific logic
} else {
console.log('Running in the main window');
// Implement redirect-based flow
}
Closing the Yapp
try {
sdk.close('https://parent-origin.com');
} catch (error) {
console.error('Failed to close:', error);
}
🔒 Security Best Practices
Token Validation
- Always validate JWT tokens using
sdk.verify()
- Verify the
aud
claim matches your Yapp's ENS name
- Always validate JWT tokens using
Origin Security
- Use HTTPS in production
- Validate message origins in iframe mode
- Set appropriate Content Security Policy (CSP) headers
Payment Handling
- Store
memo
values securely - Implement proper error handling
- Use timeouts appropriately (default: 5 minutes)
- Store
Cross-Origin Communication
- Only accept messages from configured origins
- Validate all incoming messages
- Use secure postMessage communication
📚 API Reference
For detailed API documentation, please run:
npm run docs
This will generate comprehensive API documentation in the docs
directory.
🤝 Contributing
We welcome contributions! Please see our contributing guidelines for more details.
📄 License
MIT License - see LICENSE file for details.
4 months ago