mirror of
https://github.com/hanzoai/account-sdk.git
synced 2026-09-24 16:55:42 +00:00
[TRIVIAL] Add telemetry to payment interface (#28)
* Update pay function to use payerInfo with callbackUrl - Replace infoRequests with payerInfo containing requests and callbackUrl - Update PaymentOptions interface to use payerInfo structure - Add callbackUrl to dataCallback capability in wallet_sendCalls - Update all tests to use new payerInfo structure - Update playground to use new payerInfo structure with callback URL - Update documentation and examples * Rename 'request' to 'type' in InfoRequest interface - Update InfoRequest interface to use 'type' field instead of 'request' - Update all usages in code, tests, and documentation - Maintain consistency with the new API design * Update callbackUrl to callbackURL to match spec - Rename callbackUrl to callbackURL in PayerInfo interface - Update all usages in code to use callbackURL - Update all tests to use callbackURL - Update documentation and examples to use callbackURL - Update capability object to use callbackURL consistently * refactor to payerInfoResponses * add telemetry to the payment interface * add cca parameters
This commit is contained in:
@@ -0,0 +1,147 @@
|
||||
import { ActionType, AnalyticsEventImportance, ComponentType, logEvent } from '../logEvent.js';
|
||||
|
||||
export const logPaymentStarted = ({
|
||||
amount,
|
||||
testnet,
|
||||
correlationId,
|
||||
}: {
|
||||
amount: string;
|
||||
testnet: boolean;
|
||||
correlationId: string | undefined;
|
||||
}) => {
|
||||
logEvent(
|
||||
'payment.pay.started',
|
||||
{
|
||||
action: ActionType.process,
|
||||
componentType: ComponentType.unknown,
|
||||
method: 'pay',
|
||||
correlationId,
|
||||
signerType: 'base-account',
|
||||
amount,
|
||||
testnet,
|
||||
},
|
||||
AnalyticsEventImportance.high
|
||||
);
|
||||
};
|
||||
|
||||
export const logPaymentError = ({
|
||||
amount,
|
||||
testnet,
|
||||
correlationId,
|
||||
errorMessage,
|
||||
}: {
|
||||
amount: string;
|
||||
testnet: boolean;
|
||||
correlationId: string | undefined;
|
||||
errorMessage: string;
|
||||
}) => {
|
||||
logEvent(
|
||||
'payment.pay.error',
|
||||
{
|
||||
action: ActionType.error,
|
||||
componentType: ComponentType.unknown,
|
||||
method: 'pay',
|
||||
correlationId,
|
||||
signerType: 'base-account',
|
||||
amount,
|
||||
testnet,
|
||||
errorMessage,
|
||||
},
|
||||
AnalyticsEventImportance.high
|
||||
);
|
||||
};
|
||||
|
||||
export const logPaymentCompleted = ({
|
||||
amount,
|
||||
testnet,
|
||||
correlationId,
|
||||
}: {
|
||||
amount: string;
|
||||
testnet: boolean;
|
||||
correlationId: string | undefined;
|
||||
}) => {
|
||||
logEvent(
|
||||
'payment.pay.completed',
|
||||
{
|
||||
action: ActionType.process,
|
||||
componentType: ComponentType.unknown,
|
||||
method: 'pay',
|
||||
correlationId,
|
||||
signerType: 'base-account',
|
||||
amount,
|
||||
testnet,
|
||||
},
|
||||
AnalyticsEventImportance.high
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
|
||||
export const logPaymentStatusCheckStarted = ({
|
||||
testnet,
|
||||
correlationId,
|
||||
}: {
|
||||
testnet: boolean;
|
||||
correlationId: string | undefined;
|
||||
}) => {
|
||||
logEvent(
|
||||
'payment.status_check.started',
|
||||
{
|
||||
action: ActionType.process,
|
||||
componentType: ComponentType.unknown,
|
||||
method: 'getPaymentStatus',
|
||||
correlationId,
|
||||
signerType: 'base-account',
|
||||
testnet,
|
||||
},
|
||||
AnalyticsEventImportance.low
|
||||
);
|
||||
};
|
||||
|
||||
export const logPaymentStatusCheckCompleted = ({
|
||||
testnet,
|
||||
status,
|
||||
correlationId,
|
||||
}: {
|
||||
testnet: boolean;
|
||||
status: string;
|
||||
correlationId: string | undefined;
|
||||
}) => {
|
||||
logEvent(
|
||||
'payment.status_check.completed',
|
||||
{
|
||||
action: ActionType.process,
|
||||
componentType: ComponentType.unknown,
|
||||
method: 'getPaymentStatus',
|
||||
correlationId,
|
||||
signerType: 'base-account',
|
||||
testnet,
|
||||
status,
|
||||
},
|
||||
AnalyticsEventImportance.low
|
||||
);
|
||||
};
|
||||
|
||||
export const logPaymentStatusCheckError = ({
|
||||
testnet,
|
||||
correlationId,
|
||||
errorMessage,
|
||||
}: {
|
||||
testnet: boolean;
|
||||
correlationId: string | undefined;
|
||||
errorMessage: string;
|
||||
}) => {
|
||||
logEvent(
|
||||
'payment.status_check.error',
|
||||
{
|
||||
action: ActionType.error,
|
||||
componentType: ComponentType.unknown,
|
||||
method: 'getPaymentStatus',
|
||||
correlationId,
|
||||
errorMessage,
|
||||
signerType: 'base-account',
|
||||
testnet,
|
||||
},
|
||||
AnalyticsEventImportance.low
|
||||
);
|
||||
};
|
||||
@@ -64,6 +64,10 @@ type CCAEventData = {
|
||||
dialogContext?: string;
|
||||
dialogAction?: string;
|
||||
enableAutoSubAccounts?: boolean;
|
||||
// Payment-specific attributes
|
||||
amount?: string;
|
||||
testnet?: boolean;
|
||||
status?: string;
|
||||
};
|
||||
|
||||
type AnalyticsEventData = {
|
||||
@@ -106,3 +110,4 @@ export function identify(event: CCAEventData) {
|
||||
|
||||
export { ActionType, AnalyticsEventImportance, ComponentType };
|
||||
export type { AnalyticsEventData, CCAEventData, LogEvent };
|
||||
|
||||
|
||||
@@ -5,12 +5,23 @@ import type { PaymentStatus } from './types.js';
|
||||
// Mock fetch globally
|
||||
global.fetch = vi.fn();
|
||||
|
||||
// Mock telemetry events
|
||||
vi.mock(':core/telemetry/events/payment.js', () => ({
|
||||
logPaymentStatusCheckStarted: vi.fn(),
|
||||
logPaymentStatusCheckCompleted: vi.fn(),
|
||||
logPaymentStatusCheckError: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('getPaymentStatus', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Mock console methods to avoid noise in tests
|
||||
vi.spyOn(console, 'log').mockImplementation(() => {});
|
||||
vi.spyOn(console, 'error').mockImplementation(() => {});
|
||||
// Mock crypto.randomUUID
|
||||
vi.stubGlobal('crypto', {
|
||||
randomUUID: vi.fn().mockReturnValue('mock-correlation-id'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should return completed status for successful payment', async () => {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import type { Hex } from 'viem';
|
||||
import { decodeEventLog, formatUnits } from 'viem';
|
||||
|
||||
import { logPaymentStatusCheckCompleted, logPaymentStatusCheckError, logPaymentStatusCheckStarted } from ':core/telemetry/events/payment.js';
|
||||
import { ERC20_TRANSFER_ABI, TOKENS } from './constants.js';
|
||||
import type { PaymentStatus, PaymentStatusOptions } from './types.js';
|
||||
|
||||
@@ -20,6 +21,12 @@ import type { PaymentStatus, PaymentStatusOptions } from './types.js';
|
||||
*/
|
||||
export async function getPaymentStatus(options: PaymentStatusOptions): Promise<PaymentStatus> {
|
||||
const { id, testnet = false } = options;
|
||||
|
||||
// Generate correlation ID for this status check
|
||||
const correlationId = crypto.randomUUID();
|
||||
|
||||
// Log status check started
|
||||
logPaymentStatusCheckStarted({ testnet, correlationId });
|
||||
|
||||
try {
|
||||
// Get the bundler URL based on network
|
||||
@@ -44,11 +51,13 @@ export async function getPaymentStatus(options: PaymentStatusOptions): Promise<P
|
||||
// Handle RPC errors
|
||||
if (receipt.error) {
|
||||
console.error('[getPaymentStatus] RPC error:', receipt.error);
|
||||
const errorMessage = receipt.error.message || 'Network error';
|
||||
logPaymentStatusCheckError({ testnet, correlationId, errorMessage });
|
||||
return {
|
||||
status: 'failed',
|
||||
id: id as Hex,
|
||||
message: 'Unable to check payment status. Please try again later.',
|
||||
error: receipt.error.message || 'Network error',
|
||||
error: errorMessage,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -70,6 +79,7 @@ export async function getPaymentStatus(options: PaymentStatusOptions): Promise<P
|
||||
|
||||
if (userOpResponse.result) {
|
||||
// UserOp exists but no receipt yet - it's pending
|
||||
logPaymentStatusCheckCompleted({ testnet, status: 'pending', correlationId });
|
||||
const result = {
|
||||
status: 'pending' as const,
|
||||
id: id as Hex,
|
||||
@@ -80,6 +90,7 @@ export async function getPaymentStatus(options: PaymentStatusOptions): Promise<P
|
||||
}
|
||||
|
||||
// Not found at all
|
||||
logPaymentStatusCheckCompleted({ testnet, status: 'not_found', correlationId });
|
||||
const result = {
|
||||
status: 'not_found' as const,
|
||||
id: id as Hex,
|
||||
@@ -132,6 +143,7 @@ export async function getPaymentStatus(options: PaymentStatusOptions): Promise<P
|
||||
}
|
||||
}
|
||||
|
||||
logPaymentStatusCheckCompleted({ testnet, status: 'completed', correlationId });
|
||||
const result = {
|
||||
status: 'completed' as const,
|
||||
id: id as Hex,
|
||||
@@ -155,6 +167,7 @@ export async function getPaymentStatus(options: PaymentStatusOptions): Promise<P
|
||||
}
|
||||
}
|
||||
|
||||
logPaymentStatusCheckCompleted({ testnet, status: 'failed', correlationId });
|
||||
const result = {
|
||||
status: 'failed' as const,
|
||||
id: id as Hex,
|
||||
@@ -167,11 +180,14 @@ export async function getPaymentStatus(options: PaymentStatusOptions): Promise<P
|
||||
} catch (error) {
|
||||
console.error('[getPaymentStatus] Error checking status:', error);
|
||||
|
||||
const errorMessage = error instanceof Error ? error.message : 'Connection error';
|
||||
logPaymentStatusCheckError({ testnet, correlationId, errorMessage });
|
||||
|
||||
const result = {
|
||||
status: 'failed' as const,
|
||||
id: id as Hex,
|
||||
message: 'Unable to check payment status',
|
||||
error: error instanceof Error ? error.message : 'Connection error',
|
||||
error: errorMessage,
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -9,9 +9,20 @@ vi.mock('./utils/validation.js');
|
||||
vi.mock('./utils/translatePayment.js');
|
||||
vi.mock('./utils/sdkManager.js');
|
||||
|
||||
// Mock telemetry events
|
||||
vi.mock(':core/telemetry/events/payment.js', () => ({
|
||||
logPaymentStarted: vi.fn(),
|
||||
logPaymentError: vi.fn(),
|
||||
logPaymentCompleted: vi.fn(),
|
||||
}));
|
||||
|
||||
describe('pay', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
// Mock crypto.randomUUID
|
||||
vi.stubGlobal('crypto', {
|
||||
randomUUID: vi.fn().mockReturnValue('mock-correlation-id'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should successfully process a payment', async () => {
|
||||
@@ -58,6 +69,19 @@ describe('pay', () => {
|
||||
false,
|
||||
undefined
|
||||
);
|
||||
|
||||
// Verify telemetry events
|
||||
const { logPaymentStarted, logPaymentCompleted } = await import(':core/telemetry/events/payment.js');
|
||||
expect(logPaymentStarted).toHaveBeenCalledWith({
|
||||
amount: '10.50',
|
||||
testnet: false,
|
||||
correlationId: 'mock-correlation-id',
|
||||
});
|
||||
expect(logPaymentCompleted).toHaveBeenCalledWith({
|
||||
amount: '10.50',
|
||||
testnet: false,
|
||||
correlationId: 'mock-correlation-id',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle validation errors', async () => {
|
||||
@@ -76,6 +100,20 @@ describe('pay', () => {
|
||||
amount: '0',
|
||||
to: '0xFe21034794A5a574B94fE4fDfD16e005F1C96e51',
|
||||
});
|
||||
|
||||
// Verify telemetry events
|
||||
const { logPaymentStarted, logPaymentError } = await import(':core/telemetry/events/payment.js');
|
||||
expect(logPaymentStarted).toHaveBeenCalledWith({
|
||||
amount: '0',
|
||||
testnet: false,
|
||||
correlationId: 'mock-correlation-id',
|
||||
});
|
||||
expect(logPaymentError).toHaveBeenCalledWith({
|
||||
amount: '0',
|
||||
testnet: false,
|
||||
correlationId: 'mock-correlation-id',
|
||||
errorMessage: 'Invalid amount: must be greater than 0',
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle SDK execution errors', async () => {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { logPaymentCompleted, logPaymentError, logPaymentStarted } from ':core/telemetry/events/payment.js';
|
||||
import type { Address } from 'viem';
|
||||
import type { PaymentOptions, PaymentResult } from './types.js';
|
||||
import { executePaymentWithSDK } from './utils/sdkManager.js';
|
||||
@@ -31,6 +32,12 @@ import { validateAddress, validateStringAmount } from './utils/validation.js';
|
||||
*/
|
||||
export async function pay(options: PaymentOptions): Promise<PaymentResult> {
|
||||
const { amount, to, testnet = false, payerInfo } = options;
|
||||
|
||||
// Generate correlation ID for this payment request
|
||||
const correlationId = crypto.randomUUID();
|
||||
|
||||
// Log payment started
|
||||
logPaymentStarted({ amount, testnet, correlationId });
|
||||
|
||||
try {
|
||||
validateStringAmount(amount, 2);
|
||||
@@ -42,6 +49,9 @@ export async function pay(options: PaymentOptions): Promise<PaymentResult> {
|
||||
// Step 3: Execute payment with SDK
|
||||
const executionResult = await executePaymentWithSDK(requestParams, testnet);
|
||||
|
||||
// Log payment completed
|
||||
logPaymentCompleted({ amount, testnet, correlationId });
|
||||
|
||||
// Return success result
|
||||
return {
|
||||
success: true,
|
||||
@@ -70,6 +80,9 @@ export async function pay(options: PaymentOptions): Promise<PaymentResult> {
|
||||
}
|
||||
}
|
||||
|
||||
// Log payment error
|
||||
logPaymentError({ amount, testnet, correlationId, errorMessage });
|
||||
|
||||
// Return error result
|
||||
return {
|
||||
success: false,
|
||||
|
||||
Reference in New Issue
Block a user