Skip to main content
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

1

Initialize SDK

Set up the Rise SDK with authentication
2

Create Payment

Use SDK to create payment drafts with typed data
3

Execute Payment

SDK automatically handles signing and execution
4

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: 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:
OptionDescriptionUse Case
pay_now: truePayment executes immediatelyInstant payroll, urgent payments
pay_now: falsePayment 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 MessageDescriptionSolution
insufficient balanceNot enough funds in source accountCheck entity balance before creating payments
invalid signatureTypedData signature verification failedSDK handles signing automatically
expired deadlinePayment deadline has passedCreate a new payment with updated deadline
invalid recipientRecipient address is not validVerify recipient addresses and permissions
payment limit exceededPayment amount exceeds limitsCheck 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

Need help? See the Payments page for detailed payment concepts or contact support at Hello@Riseworks.io
I