Rise provides a comprehensive payment system that enables batch payments with blockchain security. This guide covers the complete payment flow using the Rise SDK for seamless integration.
Payment Flow Overview
Initialize SDK
Set up the Rise SDK with authentication
Create Payment
Use SDK to create payment drafts with typed data
Execute Payment
SDK automatically handles signing and execution
Monitor Status
Track payment status and confirmations
Payment Types
Batch Payments
Process multiple payments in a single transaction for efficiency and cost savings.
Individual Payments
Process single payments with immediate execution.
SDK Installation
First, install the Rise SDK:
npm install @riseworks/sdk
Creating Payments
Step 1: Initialize the SDK
const { RiseApiClient } = require('@riseworks/sdk');
require('dotenv').config();
// Initialize SDK with private key for automatic signing
const client = new RiseApiClient({
environment: 'prod',
riseIdAuth: {
riseId: process.env.RISE_ID,
privateKey: process.env.PRIVATE_KEY // Required for automatic signing
}
});
// Alternative: Initialize with JWT token
const jwtClient = new RiseApiClient({
environment: 'prod',
jwtToken: process.env.JWT_TOKEN
});
Step 2: Create and Execute Payment
The SDK provides two methods for creating payments:
Method 1: Automatic Payment (Recommended)
The SDK must be initialized with a private key for automatic signing:
// Initialize SDK with private key for automatic signing
const client = new RiseApiClient({
environment: 'prod',
riseIdAuth: {
riseId: process.env.RISE_ID,
privateKey: process.env.PRIVATE_KEY // Required for automatic signing
}
});
// SDK handles everything automatically
const payment = await client.payments.sendPayment({
from: 'te-abc123def456',
to: [
{
to: 'us-xyz789abc123',
amount_cents: 10000,
currency_symbol: 'USD',
invoice_description: 'Salary payment for January 2024'
},
{
to: 'us-def456ghi789',
amount_cents: 15000,
currency_symbol: 'USD',
invoice_description: 'Bonus payment'
}
],
pay_now: true,
network: 'arbitrum'
});
console.log('Payment executed:', payment.data);
Method 2: Manual Flow (Advanced)
For manual control over the signing process:
// Initialize SDK (private key not required for manual flow)
const client = new RiseApiClient({
environment: 'prod',
jwtToken: process.env.JWT_TOKEN // JWT token is sufficient for manual flow
});
// Step 1: Get typed data for signing
const typedDataResponse = await client.payments.getPaymentTypedData({
from: 'te-abc123def456',
to: [
{
to: 'us-xyz789abc123',
amount_cents: 10000,
currency_symbol: 'USD',
invoice_description: 'Salary payment for January 2024'
}
],
pay_now: true,
network: 'arbitrum'
});
// Step 2: Sign the typed data (if you want custom signing logic)
const { ethers } = require('ethers');
const wallet = new ethers.Wallet(process.env.PRIVATE_KEY);
const signature = await wallet.signTypedData(
typedDataResponse.data.domain,
typedDataResponse.data.types,
typedDataResponse.data.typed_data
);
// Step 3: Execute with signature
const executeResponse = await client.payments.executePaymentWithSignedData({
from: 'te-abc123def456',
to: [
{
to: 'us-xyz789abc123',
amount_cents: 10000,
currency_symbol: 'USD',
invoice_description: 'Salary payment for January 2024'
}
],
pay_now: true,
network: 'arbitrum',
signer: wallet.address,
typed_data: typedDataResponse.data.typed_data,
signature: signature
});
console.log('Payment executed:', executeResponse.data);
Complete Payment Integration Example
Here’s a complete example using the Rise SDK:
const { RiseApiClient } = require('@riseworks/sdk');
require('dotenv').config();
class RisePaymentSystem {
constructor() {
this.client = new RiseApiClient({
environment: 'prod',
riseIdAuth: {
riseId: process.env.RISE_ID,
privateKey: process.env.PRIVATE_KEY
}
});
}
async createBatchPayment(teamNanoid, recipients, payNow = true) {
try {
console.log('Creating batch payment...');
const payment = await this.client.payments.sendPayment({
from: teamNanoid,
to: recipients,
pay_now: payNow,
network: 'arbitrum'
});
console.log('Payment executed successfully:', payment.data);
return payment.data;
} catch (error) {
console.error('Payment failed:', error.message);
throw error;
}
}
async queryPayments(teamNanoid, options = {}) {
try {
const params = {
team_nanoid: teamNanoid,
state: options.state || 'all',
query_type: options.queryType || 'payable',
...(options.startDate && { start_date: options.startDate }),
...(options.endDate && { end_date: options.endDate }),
...(options.recipient && { recipient: options.recipient })
};
const payments = await this.client.payments.get(params);
return payments.data;
} catch (error) {
console.error('Failed to query payments:', error.message);
throw error;
}
}
async getPaymentHistory(teamNanoid, startDate, endDate) {
return this.queryPayments(teamNanoid, {
startDate,
endDate,
state: 'all'
});
}
}
// Usage example
const paymentSystem = new RisePaymentSystem();
const recipients = [
{
to: 'us-jRxg2LRL54DJ',
amount_cents: 300,
currency_symbol: 'USD',
invoice_description: 'Salary payment'
},
{
to: 'us-d6JHBF2kuZjE',
amount_cents: 700,
currency_symbol: 'USD',
invoice_description: 'Bonus payment'
}
];
// Create immediate payment
const payment = await paymentSystem.createBatchPayment(
'te-bXy7gjb_Iga-',
recipients,
true // pay immediately
);
// Query payment history
const history = await paymentSystem.getPaymentHistory(
'te-bXy7gjb_Iga-',
'2024-01-01',
'2024-01-31'
);
Payment Status Tracking
Monitor payment status using the SDK:
class PaymentTracker {
constructor() {
this.client = new RiseApiClient({
environment: 'prod',
riseIdAuth: {
riseId: process.env.RISE_ID,
privateKey: process.env.PRIVATE_KEY
}
});
}
async queryPayments(teamNanoid, options = {}) {
const params = {
team_nanoid: teamNanoid,
state: options.state || 'all',
query_type: options.queryType || 'payable',
...(options.startDate && { start_date: options.startDate }),
...(options.endDate && { end_date: options.endDate }),
...(options.recipient && { recipient: options.recipient })
};
return await this.client.payments.get(params);
}
async waitForConfirmation(transactionHash, maxAttempts = 30) {
const { ethers } = require('ethers');
const provider = new ethers.JsonRpcProvider('https://arb1.arbitrum.io/rpc');
for (let i = 0; i < maxAttempts; i++) {
const receipt = await provider.getTransactionReceipt(transactionHash);
if (receipt && receipt.confirmations > 0) {
return {
confirmed: true,
blockNumber: receipt.blockNumber,
gasUsed: receipt.gasUsed.toString(),
status: receipt.status === 1 ? 'success' : 'failed'
};
}
// Wait 10 seconds before next check
await new Promise(resolve => setTimeout(resolve, 10000));
}
throw new Error('Transaction confirmation timeout');
}
}
Payment Timing Options
Rise supports two payment timing options:
| Option | Description | Use Case |
|---|
pay_now: true | Payment executes immediately | Instant payroll, urgent payments |
pay_now: false | Payment intent (scheduled) | Future payroll, scheduled payments |
Error Handling
The SDK provides comprehensive error handling:
try {
const payment = await client.payments.sendPayment({
from: 'te-abc123def456',
to: recipients,
pay_now: true,
network: 'arbitrum'
});
console.log('Payment successful:', payment.data);
} catch (error) {
console.error('Payment error:', error.message);
// Handle specific error types based on message content
if (error.message.includes('insufficient balance')) {
console.error('Insufficient balance in the source account');
} else if (error.message.includes('invalid signature')) {
console.error('Signature verification failed');
} else if (error.message.includes('expired')) {
console.error('Payment deadline has expired');
} else if (error.message.includes('401')) {
console.error('Authentication failed');
} else if (error.message.includes('403')) {
console.error('Insufficient permissions');
} else {
console.error('Payment failed:', error.message);
}
}
Common payment errors:
| Error Message | Description | Solution |
|---|
insufficient balance | Not enough funds in source account | Check entity balance before creating payments |
invalid signature | TypedData signature verification failed | SDK handles signing automatically |
expired deadline | Payment deadline has passed | Create a new payment with updated deadline |
invalid recipient | Recipient address is not valid | Verify recipient addresses and permissions |
payment limit exceeded | Payment amount exceeds limits | Check payment limits for the entity |
Security Best Practices
Always verify payment details before execution. Double-check amounts, recipients, and payment descriptions.
- SDK Authentication: Use the SDK’s built-in authentication and signing
- Environment Variables: Store sensitive credentials securely
- Secondary Wallets: Use dedicated wallets for API operations
- Amount Validation: Validate payment amounts before submission
- Recipient Verification: Ensure recipients have valid Rise accounts
- Error Handling: Implement proper error handling for payment failures
Testing Payments
For testing, use the staging environment:
// Staging configuration
const stagingClient = new RiseApiClient({
environment: 'stg',
riseIdAuth: {
riseId: process.env.RISE_ID,
privateKey: process.env.PRIVATE_KEY
}
});
// Test payment with small amounts
const testRecipients = [
{
to: 'us-test-user-nanoid',
amount_cents: 100,
currency_symbol: 'USD',
invoice_description: 'Test payment'
}
];
const testPayment = await stagingClient.payments.sendPayment({
from: 'te-test-team-nanoid',
to: testRecipients,
pay_now: true,
network: 'arbitrum'
});
Real-World Integration Example
Payroll System Integration
class PayrollSystem {
constructor() {
this.client = new RiseApiClient({
environment: 'prod',
riseIdAuth: {
riseId: process.env.RISE_ID,
privateKey: process.env.PRIVATE_KEY
}
});
}
async processPayrollRun(payrollRun) {
const recipients = payrollRun.employees.map(emp => ({
to: emp.user_nanoid,
amount_cents: emp.amount_cents,
currency_symbol: 'USD',
invoice_description: `${emp.description} - ${payrollRun.pay_date}`
}));
try {
const result = await this.client.payments.sendPayment({
from: payrollRun.team_nanoid,
to: recipients,
pay_now: true,
network: 'arbitrum'
});
// Store payroll run result
await this.storePayrollResult(payrollRun.id, result);
return result;
} catch (error) {
console.error(`Payroll run ${payrollRun.id} failed:`, error.message);
throw error;
}
}
async storePayrollResult(payrollId, result) {
// Store in your database
console.log(`Payroll ${payrollId} processed:`, result.data);
}
}
// Usage
const payrollSystem = new PayrollSystem();
const payrollRun = {
id: 'payroll-2024-01',
employees: [
{
user_nanoid: 'us-emp1',
amount_cents: 50000,
description: 'January Salary'
},
{
user_nanoid: 'us-emp2',
amount_cents: 45000,
description: 'January Salary'
}
],
team_nanoid: 'te-company-team',
pay_date: '2024-01-31'
};
const result = await payrollSystem.processPayrollRun(payrollRun);
Next Steps