Payment Service
The Payment Service handles payments for process execution. It uses an escrow system implemented in the payment.move
contract. The AptosPaymentService
class (src/payment/aptos-payment-service.ts
) provides the TypeScript interface.
Key Functions
makePayment(account: AptosAccount, request: PaymentRequest): Promise<string>
: Makes a payment for a process. The funds are held in escrow.verifyPayment(userAddress: string, processId: string): Promise<boolean>
: Verifies that a payment has been made by a user for a specific process.getPaymentDetails(userAddress: string, processId: string): Promise<PaymentDetails | null>
: Retrieves details about a payment.releasePayment(account: AptosAccount, request: PaymentReleaseRequest): Promise<string>
: Releases the payment from escrow to the process owner (typically after task completion and approval).requestRefund(account: AptosAccount, request: RefundRequest): Promise<string>
: Requests a refund for a payment (if the task was not completed or was canceled).
Move Contract Functions
The payment functionality is implemented in the payment.move
contract.
Functions (Move - payment.move
)
initialize(account: &signer)
: Initializes the payment module.make_payment(account: &signer, process_id: String, task_id: String, amount: u64)
: Makes a payment and places it in escrow.release_payment(account: &signer, process_id: String, task_id: String)
: Releases the payment from escrow to the process owner.request_refund(account: &signer, process_id: String, task_id: String)
: Requests a refund for a payment.verify_payment(process_id: String, task_id: String, payer_addr: address): bool
: Checks if a payment has been made.payment_exists(process_id: String, task_id: String, payer_addr: address): bool
: Checks if a payment record exists.get_payment(process_id: String, task_id: String, payer_addr: address): (String, String, address, address, u64, String, u8, u64, u64)
: Retrieves payment details.
Escrow Mechanism
The escrow mechanism is a key part of the Payment Service. It ensures that funds are securely held until a task is completed, providing protection for both users and process owners.
How Escrow Works
-
Payment: When a user wants to run a process, they call
make_payment
. The specified amount of tokens is withdrawn from the user’s account and transferred to the A3 module’s account, where it’s held in an Escrow resource. -
Payment Record: A Payment record is created, storing details like the process ID, payer, receiver, amount, currency, and status (initially ESCROW).
-
Task Completion/Approval: After the agent service completes the task, the
workflow.move
contract’sapprove_task_and_release_payment
function is called. This function verifies that:- The task exists.
- The caller is authorized to approve the task (typically the requester).
- The task is in the COMPLETED state.
- A corresponding payment exists.
-
Payment Release: If all checks pass, the
release_payment
function inpayment.move
is called. This transfers the funds from the escrow to the process owner’s account and updates the Payment record’s status to COMPLETED. -
Refunds: If a task is canceled or fails, the
request_refund
function can be called (within a specified time window). This returns the funds from escrow to the payer and updates the Payment record’s status to REFUNDED.
Payment Flow Diagram
User A3 Module Process Owner | | | |--- make_payment ------>| | | | | |<-- Payment in Escrow --| | | | | | | (Task Execution) | | | | |--- approve_task ------>| | | | | | |--- release_payment ---->| | | | |<-- Payment Complete -->|<-- Payment Received ----|
Payment Statuses
ESCROW (0)
: The payment has been made and is being held in escrow.COMPLETED (1)
: The payment has been released to the process owner.REFUNDED (2)
: The payment has been refunded to the payer.CANCELED (3)
: The payment has been canceled.
Example Usage
Making a Payment
import { createAptosPaymentService, PaymentRequest } from 'platform';import { AptosAccount } from '@aptos-labs/ts-sdk';
// Create a payment service instanceconst paymentService = createAptosPaymentService({ privateKey: process.env.APTOS_PRIVATE_KEY, moduleAddress: process.env.APTOS_MODULE_ADDRESS,});
// Create an Aptos account from a private keyconst account = new AptosAccount(process.env.APTOS_PRIVATE_KEY);
// Define the payment requestconst request: PaymentRequest = { processId: 'my-process-id', taskId: 'task-1', // Can be the same as the transaction ID amount: '1000000', // In octas (1 APT = 100,000,000 octas)};
// Make the paymenttry { const transactionHash = await paymentService.makePayment(account, request); console.log(`Payment made. Transaction hash: ${transactionHash}`);} catch (error) { console.error('Failed to make payment:', error);}
Verifying a Payment
// Verify that a payment has been madeconst userAddress = '0x123...';const processId = 'my-process-id';
const isVerified = await paymentService.verifyPayment(userAddress, processId);if (isVerified) { console.log('Payment verified.');} else { console.log('No payment found or payment not in escrow.');}
Getting Payment Details
// Get payment detailsconst paymentDetails = await paymentService.getPaymentDetails(userAddress, processId);if (paymentDetails) { console.log('Payment details:', paymentDetails); // { // processId: 'my-process-id', // taskId: 'task-1', // payer: '0x123...', // receiver: '0x456...', // amount: '1000000', // currency: 'APT', // status: 0, // ESCROW // createdAt: 1626100000, // updatedAt: 1626100000 // }} else { console.log('No payment found.');}
Releasing a Payment
// This is typically called automatically after task completion// But can also be called manually if neededconst releaseRequest = { processId: 'my-process-id', taskId: 'task-1',};
try { const transactionHash = await paymentService.releasePayment(account, releaseRequest); console.log(`Payment released. Transaction hash: ${transactionHash}`);} catch (error) { console.error('Failed to release payment:', error);}
Requesting a Refund
// Can be called if the task was not completed or was canceledconst refundRequest = { processId: 'my-process-id', taskId: 'task-1',};
try { const transactionHash = await paymentService.requestRefund(account, refundRequest); console.log(`Refund requested. Transaction hash: ${transactionHash}`);} catch (error) { console.error('Failed to request refund:', error);}
Integration with Agent Gateway
The Payment Service is integrated with the Agent Gateway to verify payments before executing processes:
// In the Agent Gateway's execute endpoint handlerapp.post('/api/execute/:processId', async (req, res) => { const processId = req.params.processId; const { userAddress } = req.body;
try { // Get process details to check if payment is required const process = await discoveryService.getProcess(processId); if (!process) { return res.status(404).json({ success: false, error: 'Process not found', processId, }); }
// Check if payment is required if (process.pricing && process.pricing.requiresPrepayment) { // Verify payment const isVerified = await paymentService.verifyPayment(userAddress, processId); if (!isVerified) { return res.status(400).json({ success: false, error: 'Payment required', processId, }); } }
// Continue with process execution... } catch (error) { console.error('Error processing request:', error); res.status(500).json({ success: false, error: 'Internal server error', timestamp: new Date().toISOString(), }); }});
API Endpoints
The Agent Gateway provides the following API endpoints for payment-related operations:
Make a Payment
POST /api/payment
Request Body:
{ "processId": "string", "amount": "string", "userAddress": "string"}
Response:
{ "success": true, "transactionHash": "string", "processId": "string", "amount": "string", "userAddress": "string", "timestamp": "date"}
Verify a Payment
GET /api/payment/verify/:processId/:userAddress
Response:
{ "success": true, "verified": true, "processId": "string", "userAddress": "string", "timestamp": "date"}