arispay 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +4 -0
- package/dist/chunk-4ON4ZXR3.js +953 -0
- package/dist/cli-b.js +277 -0
- package/dist/cli.js +426 -0
- package/dist/index.d.ts +216 -0
- package/dist/index.js +406 -0
- package/package.json +31 -0
- package/src/agents.ts +117 -0
- package/src/cli-b.ts +373 -0
- package/src/cli-shared.ts +650 -0
- package/src/cli.ts +439 -0
- package/src/client.ts +65 -0
- package/src/index.ts +98 -0
- package/src/payments.ts +22 -0
- package/src/transactions.ts +19 -0
- package/src/users.ts +67 -0
- package/src/webhooks.ts +81 -0
- package/tsconfig.json +8 -0
- package/tsup.config.ts +18 -0
package/src/users.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
CreateUserRequest,
|
|
3
|
+
UserResponse,
|
|
4
|
+
SetupPaymentMethodResponse,
|
|
5
|
+
WalletPaymentMethodResponse,
|
|
6
|
+
WalletStatusResponse,
|
|
7
|
+
SetLimitsRequest,
|
|
8
|
+
SpendLimitsResponse,
|
|
9
|
+
} from '@arispay/shared';
|
|
10
|
+
import type { HttpClient } from './client.js';
|
|
11
|
+
|
|
12
|
+
export class UserService {
|
|
13
|
+
constructor(private client: HttpClient) {}
|
|
14
|
+
|
|
15
|
+
async create(params: CreateUserRequest): Promise<UserResponse> {
|
|
16
|
+
return this.client.post<UserResponse>('/v1/users', params);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async get(userId: string): Promise<UserResponse> {
|
|
20
|
+
return this.client.get<UserResponse>(`/v1/users/${userId}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async getByExternalId(externalId: string): Promise<UserResponse> {
|
|
24
|
+
return this.client.get<UserResponse>(`/v1/users/by-external/${encodeURIComponent(externalId)}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async getWalletStatus(userId: string): Promise<WalletStatusResponse> {
|
|
28
|
+
return this.client.get<WalletStatusResponse>(`/v1/users/${userId}/wallet-status`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Hosted card flow — returns setupUrl for redirect */
|
|
32
|
+
async addPaymentMethod(
|
|
33
|
+
params: { userId: string; type: 'card'; returnUrl: string },
|
|
34
|
+
): Promise<SetupPaymentMethodResponse>;
|
|
35
|
+
/** Direct card tokenization — returns user with card attached */
|
|
36
|
+
async addPaymentMethod(
|
|
37
|
+
params: {
|
|
38
|
+
userId: string;
|
|
39
|
+
type: 'card';
|
|
40
|
+
cardNumber: string;
|
|
41
|
+
expiryMonth: string;
|
|
42
|
+
expiryYear: string;
|
|
43
|
+
securityCode: string;
|
|
44
|
+
},
|
|
45
|
+
): Promise<UserResponse>;
|
|
46
|
+
async addPaymentMethod(
|
|
47
|
+
params: { userId: string; type: 'wallet'; walletAddress: string; chain: string },
|
|
48
|
+
): Promise<WalletPaymentMethodResponse>;
|
|
49
|
+
async addPaymentMethod(
|
|
50
|
+
params: { userId: string; type: string; [key: string]: unknown },
|
|
51
|
+
): Promise<SetupPaymentMethodResponse | UserResponse | WalletPaymentMethodResponse> {
|
|
52
|
+
const { userId, ...body } = params;
|
|
53
|
+
return this.client.post(`/v1/users/${userId}/payment-methods`, body);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async completePaymentMethod(
|
|
57
|
+
params: { userId: string; sessionId: string },
|
|
58
|
+
): Promise<UserResponse> {
|
|
59
|
+
const { userId, ...body } = params;
|
|
60
|
+
return this.client.post<UserResponse>(`/v1/users/${userId}/payment-methods/complete`, body);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async setLimits(params: { userId: string } & SetLimitsRequest): Promise<SpendLimitsResponse> {
|
|
64
|
+
const { userId, ...body } = params;
|
|
65
|
+
return this.client.put<SpendLimitsResponse>(`/v1/users/${userId}/limits`, body);
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/webhooks.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { createHmac, timingSafeEqual } from 'crypto';
|
|
2
|
+
import type { RegisterWebhookRequest, WebhookResponse } from '@arispay/shared';
|
|
3
|
+
import type { HttpClient } from './client.js';
|
|
4
|
+
|
|
5
|
+
/** Maximum allowed age for webhook timestamps (5 minutes) */
|
|
6
|
+
const WEBHOOK_TOLERANCE_SECONDS = 300;
|
|
7
|
+
|
|
8
|
+
export class WebhookService {
|
|
9
|
+
constructor(private client: HttpClient) {}
|
|
10
|
+
|
|
11
|
+
async register(params: RegisterWebhookRequest): Promise<WebhookResponse & { secret: string }> {
|
|
12
|
+
return this.client.post<WebhookResponse & { secret: string }>('/v1/webhooks', params);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async list(): Promise<WebhookResponse[]> {
|
|
16
|
+
return this.client.get<WebhookResponse[]>('/v1/webhooks');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async delete(webhookId: string): Promise<void> {
|
|
20
|
+
await this.client.delete<void>(`/v1/webhooks/${webhookId}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Verify a webhook signature from an incoming request.
|
|
25
|
+
*
|
|
26
|
+
* ArisPay signs every webhook delivery with HMAC-SHA256 using the secret
|
|
27
|
+
* returned when you registered the webhook. The signature is sent in the
|
|
28
|
+
* `X-ArisPay-Signature` header and the unix timestamp in `X-ArisPay-Timestamp`.
|
|
29
|
+
*
|
|
30
|
+
* @param body - The raw request body string (do NOT parse it first)
|
|
31
|
+
* @param signature - Value of the `X-ArisPay-Signature` header
|
|
32
|
+
* @param timestamp - Value of the `X-ArisPay-Timestamp` header
|
|
33
|
+
* @param secret - Your webhook secret (`whsec_...`)
|
|
34
|
+
* @param toleranceSec - Maximum allowed age in seconds (default: 300 = 5 min)
|
|
35
|
+
* @returns `true` if the signature is valid and the timestamp is within tolerance
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* ```ts
|
|
39
|
+
* import { WebhookService } from 'arispay';
|
|
40
|
+
*
|
|
41
|
+
* app.post('/webhooks', (req, res) => {
|
|
42
|
+
* const isValid = WebhookService.verifyWebhookSignature(
|
|
43
|
+
* req.body, // raw body string
|
|
44
|
+
* req.headers['x-arispay-signature'],
|
|
45
|
+
* req.headers['x-arispay-timestamp'],
|
|
46
|
+
* process.env.WEBHOOK_SECRET!,
|
|
47
|
+
* );
|
|
48
|
+
* if (!isValid) return res.status(401).send('Invalid signature');
|
|
49
|
+
* // Process the webhook event...
|
|
50
|
+
* });
|
|
51
|
+
* ```
|
|
52
|
+
*/
|
|
53
|
+
static verifyWebhookSignature(
|
|
54
|
+
body: string,
|
|
55
|
+
signature: string,
|
|
56
|
+
timestamp: string,
|
|
57
|
+
secret: string,
|
|
58
|
+
toleranceSec: number = WEBHOOK_TOLERANCE_SECONDS,
|
|
59
|
+
): boolean {
|
|
60
|
+
// Validate inputs
|
|
61
|
+
if (!body || !signature || !timestamp || !secret) return false;
|
|
62
|
+
|
|
63
|
+
// Check timestamp freshness (replay protection)
|
|
64
|
+
const ts = parseInt(timestamp, 10);
|
|
65
|
+
if (isNaN(ts)) return false;
|
|
66
|
+
const age = Math.abs(Math.floor(Date.now() / 1000) - ts);
|
|
67
|
+
if (age > toleranceSec) return false;
|
|
68
|
+
|
|
69
|
+
// Compute expected signature
|
|
70
|
+
const signaturePayload = `${timestamp}.${body}`;
|
|
71
|
+
const expected = createHmac('sha256', secret).update(signaturePayload).digest('hex');
|
|
72
|
+
|
|
73
|
+
// Constant-time comparison to prevent timing attacks
|
|
74
|
+
try {
|
|
75
|
+
return timingSafeEqual(Buffer.from(signature, 'hex'), Buffer.from(expected, 'hex'));
|
|
76
|
+
} catch {
|
|
77
|
+
// Buffers different length = invalid
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
package/tsconfig.json
ADDED
package/tsup.config.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { defineConfig } from 'tsup';
|
|
2
|
+
|
|
3
|
+
export default defineConfig([
|
|
4
|
+
{
|
|
5
|
+
entry: ['src/index.ts'],
|
|
6
|
+
format: ['esm'],
|
|
7
|
+
dts: true,
|
|
8
|
+
outDir: 'dist',
|
|
9
|
+
clean: true,
|
|
10
|
+
},
|
|
11
|
+
{
|
|
12
|
+
entry: ['src/cli.ts', 'src/cli-b.ts'],
|
|
13
|
+
format: ['esm'],
|
|
14
|
+
outDir: 'dist',
|
|
15
|
+
banner: { js: '#!/usr/bin/env node' },
|
|
16
|
+
noExternal: ['@arispay/shared'],
|
|
17
|
+
},
|
|
18
|
+
]);
|