@asgcard/pay 0.1.1
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/CHANGELOG.md +90 -0
- package/LICENSE +21 -0
- package/README.md +393 -0
- package/dist/cjs/adapters/base.js +174 -0
- package/dist/cjs/adapters/base.js.map +1 -0
- package/dist/cjs/adapters/evm.js +263 -0
- package/dist/cjs/adapters/evm.js.map +1 -0
- package/dist/cjs/adapters/index.js +13 -0
- package/dist/cjs/adapters/index.js.map +1 -0
- package/dist/cjs/adapters/stellar.js +173 -0
- package/dist/cjs/adapters/stellar.js.map +1 -0
- package/dist/cjs/adapters/stripe.js +338 -0
- package/dist/cjs/adapters/stripe.js.map +1 -0
- package/dist/cjs/adapters/types.js +3 -0
- package/dist/cjs/adapters/types.js.map +1 -0
- package/dist/cjs/client.js +309 -0
- package/dist/cjs/client.js.map +1 -0
- package/dist/cjs/index.js +36 -0
- package/dist/cjs/index.js.map +1 -0
- package/dist/cjs/logger.js +7 -0
- package/dist/cjs/logger.js.map +1 -0
- package/dist/cjs/mpp.js +187 -0
- package/dist/cjs/mpp.js.map +1 -0
- package/dist/cjs/policy.js +71 -0
- package/dist/cjs/policy.js.map +1 -0
- package/dist/cjs/stellar.js +87 -0
- package/dist/cjs/stellar.js.map +1 -0
- package/dist/esm/adapters/base.js +170 -0
- package/dist/esm/adapters/base.js.map +1 -0
- package/dist/esm/adapters/evm.js +258 -0
- package/dist/esm/adapters/evm.js.map +1 -0
- package/dist/esm/adapters/index.js +5 -0
- package/dist/esm/adapters/index.js.map +1 -0
- package/dist/esm/adapters/stellar.js +136 -0
- package/dist/esm/adapters/stellar.js.map +1 -0
- package/dist/esm/adapters/stripe.js +334 -0
- package/dist/esm/adapters/stripe.js.map +1 -0
- package/dist/esm/adapters/types.js +2 -0
- package/dist/esm/adapters/types.js.map +1 -0
- package/dist/esm/client.js +302 -0
- package/dist/esm/client.js.map +1 -0
- package/dist/esm/index.js +16 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/logger.js +3 -0
- package/dist/esm/logger.js.map +1 -0
- package/dist/esm/mpp.js +175 -0
- package/dist/esm/mpp.js.map +1 -0
- package/dist/esm/policy.js +67 -0
- package/dist/esm/policy.js.map +1 -0
- package/dist/esm/stellar.js +50 -0
- package/dist/esm/stellar.js.map +1 -0
- package/dist/types/adapters/base.d.ts +53 -0
- package/dist/types/adapters/base.d.ts.map +1 -0
- package/dist/types/adapters/evm.d.ts +81 -0
- package/dist/types/adapters/evm.d.ts.map +1 -0
- package/dist/types/adapters/index.d.ts +6 -0
- package/dist/types/adapters/index.d.ts.map +1 -0
- package/dist/types/adapters/stellar.d.ts +67 -0
- package/dist/types/adapters/stellar.d.ts.map +1 -0
- package/dist/types/adapters/stripe.d.ts +206 -0
- package/dist/types/adapters/stripe.d.ts.map +1 -0
- package/dist/types/adapters/types.d.ts +35 -0
- package/dist/types/adapters/types.d.ts.map +1 -0
- package/dist/types/client.d.ts +89 -0
- package/dist/types/client.d.ts.map +1 -0
- package/dist/types/index.d.ts +15 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/logger.d.ts +10 -0
- package/dist/types/logger.d.ts.map +1 -0
- package/dist/types/mpp.d.ts +153 -0
- package/dist/types/mpp.d.ts.map +1 -0
- package/dist/types/policy.d.ts +40 -0
- package/dist/types/policy.d.ts.map +1 -0
- package/dist/types/stellar.d.ts +27 -0
- package/dist/types/stellar.d.ts.map +1 -0
- package/package.json +86 -0
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StripePaymentAdapter = void 0;
|
|
4
|
+
const logger_1 = require("../logger");
|
|
5
|
+
const mpp_1 = require("../mpp");
|
|
6
|
+
/**
|
|
7
|
+
* Required Stripe API version for MPP features.
|
|
8
|
+
* @see https://docs.stripe.com/payments/machine/mpp
|
|
9
|
+
*/
|
|
10
|
+
const STRIPE_API_VERSION = '2026-03-04.preview';
|
|
11
|
+
// ─── Stripe MPP Adapter ─────────────────────────────────────────────
|
|
12
|
+
/**
|
|
13
|
+
* StripePaymentAdapter — Machine Payments Protocol (MPP) settlement.
|
|
14
|
+
*
|
|
15
|
+
* Implements the full MPP flow for Stripe:
|
|
16
|
+
* 1. Receives 402 + `WWW-Authenticate: Payment` challenge
|
|
17
|
+
* 2. Creates a Shared Payment Token (SPT) via Stripe API
|
|
18
|
+
* 3. Builds MPP Credential with SPT
|
|
19
|
+
* 4. Returns credential for `Authorization: Payment` header
|
|
20
|
+
*
|
|
21
|
+
* Works in two modes:
|
|
22
|
+
* - **Autonomous** (with paymentMethodId): Creates SPTs automatically
|
|
23
|
+
* - **Delegated** (without): Requires external SPT provisioning via `setSptToken()`
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* // Autonomous mode — agent has a payment method on file
|
|
28
|
+
* const adapter = new StripePaymentAdapter({
|
|
29
|
+
* stripeSecretKey: 'sk_test_...',
|
|
30
|
+
* networkId: 'my-network',
|
|
31
|
+
* paymentMethodId: 'pm_card_visa', // test card
|
|
32
|
+
* });
|
|
33
|
+
*
|
|
34
|
+
* // Delegated mode — external system provides SPTs
|
|
35
|
+
* const adapter = new StripePaymentAdapter({
|
|
36
|
+
* stripeSecretKey: 'sk_test_...',
|
|
37
|
+
* });
|
|
38
|
+
* adapter.setSptToken('spt_...');
|
|
39
|
+
* ```
|
|
40
|
+
*
|
|
41
|
+
* @see https://mpp.dev/payment-methods/stripe — MPP Stripe method spec
|
|
42
|
+
* @see https://docs.stripe.com/agentic-commerce/concepts/shared-payment-tokens
|
|
43
|
+
*/
|
|
44
|
+
class StripePaymentAdapter {
|
|
45
|
+
chainName = 'Stripe MPP';
|
|
46
|
+
caip2Id;
|
|
47
|
+
secretKey;
|
|
48
|
+
networkId;
|
|
49
|
+
paymentMethodTypes;
|
|
50
|
+
paymentMethodId;
|
|
51
|
+
currency;
|
|
52
|
+
sptExpirySeconds;
|
|
53
|
+
stripeApiBase;
|
|
54
|
+
log;
|
|
55
|
+
/** Externally-provided SPT for delegated mode */
|
|
56
|
+
externalSpt = null;
|
|
57
|
+
/** Last MPP challenge processed (for credential building) */
|
|
58
|
+
lastChallenge = null;
|
|
59
|
+
constructor(options) {
|
|
60
|
+
if (!options.stripeSecretKey) {
|
|
61
|
+
throw new Error('StripePaymentAdapter requires a stripeSecretKey. ' +
|
|
62
|
+
'Get one from https://dashboard.stripe.com/apikeys');
|
|
63
|
+
}
|
|
64
|
+
this.secretKey = options.stripeSecretKey;
|
|
65
|
+
this.networkId = options.networkId ?? 'internal';
|
|
66
|
+
this.paymentMethodTypes = options.paymentMethodTypes ?? ['card'];
|
|
67
|
+
this.paymentMethodId = options.paymentMethodId ?? null;
|
|
68
|
+
this.currency = options.currency ?? 'usd';
|
|
69
|
+
this.sptExpirySeconds = options.sptExpirySeconds ?? 300;
|
|
70
|
+
this.stripeApiBase = options.stripeApiBase ?? 'https://api.stripe.com';
|
|
71
|
+
this.log = options.logger ?? logger_1.noopLogger;
|
|
72
|
+
// CAIP-2-style identifier for Stripe
|
|
73
|
+
this.caip2Id = this.secretKey.startsWith('sk_live')
|
|
74
|
+
? 'stripe:live'
|
|
75
|
+
: 'stripe:test';
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Set or update an externally-provided SPT token.
|
|
79
|
+
* Used in delegated mode when an external system provides the SPT.
|
|
80
|
+
*/
|
|
81
|
+
setSptToken(token) {
|
|
82
|
+
this.externalSpt = token;
|
|
83
|
+
this.log(`[Stripe/MPP] 🔑 External SPT set: ${token.slice(0, 16)}…`);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Store a parsed MPP challenge for credential building.
|
|
87
|
+
* Called by OwsClient when a 402 with `WWW-Authenticate: Payment method="stripe"` is received.
|
|
88
|
+
*/
|
|
89
|
+
setMppChallenge(challenge) {
|
|
90
|
+
this.lastChallenge = challenge;
|
|
91
|
+
this.log(`[Stripe/MPP] 📋 Challenge received: id=${challenge.id}, realm=${challenge.realm}`);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Get the last built MPP credential (base64url-encoded).
|
|
95
|
+
* Used by OwsClient to populate the `Authorization: Payment` header.
|
|
96
|
+
*/
|
|
97
|
+
getLastCredential() {
|
|
98
|
+
return this._lastCredential;
|
|
99
|
+
}
|
|
100
|
+
_lastCredential = null;
|
|
101
|
+
getAddress() {
|
|
102
|
+
if (this.externalSpt)
|
|
103
|
+
return `spt:${this.externalSpt.slice(0, 16)}…`;
|
|
104
|
+
if (this.paymentMethodId)
|
|
105
|
+
return `pm:${this.paymentMethodId.slice(0, 16)}…`;
|
|
106
|
+
return `stripe:${this.secretKey.slice(0, 12)}…`;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Execute a Stripe MPP payment.
|
|
110
|
+
*
|
|
111
|
+
* This method handles the full SPT lifecycle:
|
|
112
|
+
* 1. If an external SPT is set → use it directly
|
|
113
|
+
* 2. If paymentMethodId is set → create SPT autonomously via Stripe API
|
|
114
|
+
* 3. Build MPP Credential with the SPT
|
|
115
|
+
* 4. Return credential string (used as "tx hash" equivalent)
|
|
116
|
+
*
|
|
117
|
+
* @param destination - Recipient (Stripe account or payment endpoint)
|
|
118
|
+
* @param amount - Amount in smallest currency unit (e.g. cents)
|
|
119
|
+
* @param network - Network identifier (ignored for Stripe, kept for interface compat)
|
|
120
|
+
* @returns Base64url-encoded MPP credential string, or null on failure
|
|
121
|
+
*/
|
|
122
|
+
async pay(destination, amount, network) {
|
|
123
|
+
const tag = '[Stripe/MPP]';
|
|
124
|
+
try {
|
|
125
|
+
const amountInt = parseInt(amount, 10);
|
|
126
|
+
if (isNaN(amountInt) || amountInt <= 0) {
|
|
127
|
+
this.log(`${tag} ❌ Invalid amount: ${amount}`);
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
const formatted = `$${(amountInt / 100).toFixed(2)}`;
|
|
131
|
+
this.log(`${tag} 🚀 ${formatted} (${this.currency.toUpperCase()}) → ${destination.slice(0, 20)}…`);
|
|
132
|
+
// ── Step 1: Get or create SPT ──────────────────────────────
|
|
133
|
+
let sptId;
|
|
134
|
+
if (this.externalSpt) {
|
|
135
|
+
// Delegated mode: use externally-provided SPT
|
|
136
|
+
sptId = this.externalSpt;
|
|
137
|
+
this.log(`${tag} 🔑 Using external SPT: ${sptId.slice(0, 16)}…`);
|
|
138
|
+
}
|
|
139
|
+
else if (this.paymentMethodId) {
|
|
140
|
+
// Autonomous mode: create SPT via Stripe API
|
|
141
|
+
this.log(`${tag} 🔧 Creating SPT for pm: ${this.paymentMethodId.slice(0, 16)}…`);
|
|
142
|
+
const spt = await this.createSpt(amountInt, this.currency);
|
|
143
|
+
if (!spt) {
|
|
144
|
+
this.log(`${tag} ❌ SPT creation failed`);
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
sptId = spt.id;
|
|
148
|
+
this.log(`${tag} ✅ SPT created: ${sptId.slice(0, 20)}…`);
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
this.log(`${tag} ❌ No SPT and no paymentMethodId — cannot pay autonomously`);
|
|
152
|
+
return null;
|
|
153
|
+
}
|
|
154
|
+
// ── Step 2: Build MPP Credential ───────────────────────────
|
|
155
|
+
if (this.lastChallenge) {
|
|
156
|
+
// Full MPP flow: build proper credential with challenge echo
|
|
157
|
+
const credential = (0, mpp_1.buildMppCredential)(this.lastChallenge, this.getAddress(), { spt: sptId });
|
|
158
|
+
this._lastCredential = credential;
|
|
159
|
+
this.log(`${tag} 📝 Credential built (challenge: ${this.lastChallenge.id})`);
|
|
160
|
+
return credential;
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Fallback: SPT-only mode (direct Stripe API, no MPP challenge)
|
|
164
|
+
// Create PaymentIntent directly
|
|
165
|
+
this.log(`${tag} 💳 No MPP challenge — creating PaymentIntent directly`);
|
|
166
|
+
const pi = await this.createPaymentIntent(sptId, amountInt, this.currency);
|
|
167
|
+
if (!pi) {
|
|
168
|
+
this.log(`${tag} ❌ PaymentIntent creation failed`);
|
|
169
|
+
return null;
|
|
170
|
+
}
|
|
171
|
+
this.log(`${tag} ✅ PaymentIntent: ${pi.id} (${pi.status})`);
|
|
172
|
+
return pi.id;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
this.log(`${tag} ❌ ${error.message}`);
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// ─── Stripe API Methods ─────────────────────────────────────────
|
|
181
|
+
/**
|
|
182
|
+
* Create a Shared Payment Token (SPT) via Stripe API.
|
|
183
|
+
*
|
|
184
|
+
* Test mode: POST /v1/test_helpers/shared_payment/granted_tokens
|
|
185
|
+
* Live mode: POST /v1/shared_payment/issued_tokens (TBD by Stripe)
|
|
186
|
+
*/
|
|
187
|
+
async createSpt(amount, currency) {
|
|
188
|
+
const isTest = this.secretKey.startsWith('sk_test');
|
|
189
|
+
const endpoint = isTest
|
|
190
|
+
? '/v1/test_helpers/shared_payment/granted_tokens'
|
|
191
|
+
: '/v1/shared_payment/issued_tokens';
|
|
192
|
+
const expiresAt = Math.floor((Date.now() + this.sptExpirySeconds * 1000) / 1000);
|
|
193
|
+
const body = new URLSearchParams();
|
|
194
|
+
body.append('payment_method', this.paymentMethodId);
|
|
195
|
+
body.append('usage_limits[currency]', currency);
|
|
196
|
+
body.append('usage_limits[max_amount]', amount.toString());
|
|
197
|
+
body.append('usage_limits[expires_at]', expiresAt.toString());
|
|
198
|
+
try {
|
|
199
|
+
const response = await fetch(`${this.stripeApiBase}${endpoint}`, {
|
|
200
|
+
method: 'POST',
|
|
201
|
+
headers: {
|
|
202
|
+
'Authorization': `Basic ${Buffer.from(`${this.secretKey}:`).toString('base64')}`,
|
|
203
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
204
|
+
'Stripe-Version': STRIPE_API_VERSION,
|
|
205
|
+
},
|
|
206
|
+
body,
|
|
207
|
+
});
|
|
208
|
+
if (!response.ok) {
|
|
209
|
+
const error = await response.json();
|
|
210
|
+
this.log(`[Stripe/MPP] ❌ SPT API error: ${error?.error?.message || response.statusText}`);
|
|
211
|
+
return null;
|
|
212
|
+
}
|
|
213
|
+
return await response.json();
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
this.log(`[Stripe/MPP] ❌ SPT request failed: ${error.message}`);
|
|
217
|
+
return null;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Create and confirm a PaymentIntent using an SPT.
|
|
222
|
+
* Used in fallback mode (no MPP challenge, direct Stripe API).
|
|
223
|
+
*/
|
|
224
|
+
async createPaymentIntent(sptId, amount, currency) {
|
|
225
|
+
const body = new URLSearchParams();
|
|
226
|
+
body.append('amount', amount.toString());
|
|
227
|
+
body.append('currency', currency);
|
|
228
|
+
body.append('confirm', 'true');
|
|
229
|
+
body.append('automatic_payment_methods[enabled]', 'true');
|
|
230
|
+
body.append('payment_method_data[shared_payment_granted_token]', sptId);
|
|
231
|
+
body.append('metadata[protocol]', 'mpp');
|
|
232
|
+
body.append('metadata[agent]', 'asgcard-pay');
|
|
233
|
+
try {
|
|
234
|
+
const response = await fetch(`${this.stripeApiBase}/v1/payment_intents`, {
|
|
235
|
+
method: 'POST',
|
|
236
|
+
headers: {
|
|
237
|
+
'Authorization': `Basic ${Buffer.from(`${this.secretKey}:`).toString('base64')}`,
|
|
238
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
239
|
+
'Stripe-Version': STRIPE_API_VERSION,
|
|
240
|
+
},
|
|
241
|
+
body,
|
|
242
|
+
});
|
|
243
|
+
if (!response.ok) {
|
|
244
|
+
const error = await response.json();
|
|
245
|
+
this.log(`[Stripe/MPP] ❌ PI error: ${error?.error?.message || response.statusText}`);
|
|
246
|
+
return null;
|
|
247
|
+
}
|
|
248
|
+
return await response.json();
|
|
249
|
+
}
|
|
250
|
+
catch (error) {
|
|
251
|
+
this.log(`[Stripe/MPP] ❌ PI request failed: ${error.message}`);
|
|
252
|
+
return null;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
/**
|
|
256
|
+
* Create a crypto deposit-mode PaymentIntent.
|
|
257
|
+
* This is the verified-working flow for MPP on Stripe.
|
|
258
|
+
* Uses Tempo network + USDC stablecoins.
|
|
259
|
+
*
|
|
260
|
+
* @param amount - Amount in cents (e.g. 100 = $1.00)
|
|
261
|
+
* @param currency - Currency code (default: 'usd')
|
|
262
|
+
* @returns PaymentIntentResult with deposit address, or null on failure
|
|
263
|
+
*
|
|
264
|
+
* @example
|
|
265
|
+
* ```ts
|
|
266
|
+
* const pi = await adapter.createCryptoPaymentIntent(100, 'usd');
|
|
267
|
+
* // pi.next_action.crypto_display_details.deposit_addresses.tempo.address
|
|
268
|
+
* ```
|
|
269
|
+
*/
|
|
270
|
+
async createCryptoPaymentIntent(amount, currency = 'usd') {
|
|
271
|
+
const body = new URLSearchParams();
|
|
272
|
+
body.append('amount', amount.toString());
|
|
273
|
+
body.append('currency', currency);
|
|
274
|
+
body.append('payment_method_types[]', 'crypto');
|
|
275
|
+
body.append('payment_method_data[type]', 'crypto');
|
|
276
|
+
body.append('payment_method_options[crypto][mode]', 'deposit');
|
|
277
|
+
body.append('payment_method_options[crypto][deposit_options][networks][]', 'tempo');
|
|
278
|
+
body.append('confirm', 'true');
|
|
279
|
+
body.append('metadata[protocol]', 'mpp');
|
|
280
|
+
body.append('metadata[agent]', 'asgcard-pay');
|
|
281
|
+
try {
|
|
282
|
+
const response = await fetch(`${this.stripeApiBase}/v1/payment_intents`, {
|
|
283
|
+
method: 'POST',
|
|
284
|
+
headers: {
|
|
285
|
+
'Authorization': `Basic ${Buffer.from(`${this.secretKey}:`).toString('base64')}`,
|
|
286
|
+
'Content-Type': 'application/x-www-form-urlencoded',
|
|
287
|
+
'Stripe-Version': STRIPE_API_VERSION,
|
|
288
|
+
},
|
|
289
|
+
body,
|
|
290
|
+
});
|
|
291
|
+
if (!response.ok) {
|
|
292
|
+
const error = await response.json();
|
|
293
|
+
this.log(`[Stripe/MPP] ❌ Crypto PI error: ${error?.error?.message || response.statusText}`);
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
const pi = await response.json();
|
|
297
|
+
this.log(`[Stripe/MPP] ✅ Crypto PI: ${pi.id} (${pi.status})`);
|
|
298
|
+
// Extract deposit address if available
|
|
299
|
+
const depositAddr = pi.next_action?.crypto_display_details?.deposit_addresses?.tempo?.address;
|
|
300
|
+
if (depositAddr) {
|
|
301
|
+
this.log(`[Stripe/MPP] 📬 Deposit → ${depositAddr}`);
|
|
302
|
+
}
|
|
303
|
+
return pi;
|
|
304
|
+
}
|
|
305
|
+
catch (error) {
|
|
306
|
+
this.log(`[Stripe/MPP] ❌ Crypto PI request failed: ${error.message}`);
|
|
307
|
+
return null;
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
/**
|
|
311
|
+
* Build and return a fully-formed MPP Challenge for server-side use.
|
|
312
|
+
* Useful if you want to GATE your own API with MPP 402.
|
|
313
|
+
*
|
|
314
|
+
* @param amount - Amount to charge (in smallest unit)
|
|
315
|
+
* @param options - Challenge options
|
|
316
|
+
* @returns WWW-Authenticate header value
|
|
317
|
+
*/
|
|
318
|
+
buildServerChallenge(amount, options = {}) {
|
|
319
|
+
const currency = options.currency ?? this.currency;
|
|
320
|
+
const expires = new Date(Date.now() + (options.expiresInSeconds ?? this.sptExpirySeconds) * 1000).toISOString();
|
|
321
|
+
// Build the request object
|
|
322
|
+
const requestObj = {
|
|
323
|
+
amount,
|
|
324
|
+
currency,
|
|
325
|
+
description: options.description,
|
|
326
|
+
methodDetails: {
|
|
327
|
+
networkId: this.networkId,
|
|
328
|
+
paymentMethodTypes: this.paymentMethodTypes,
|
|
329
|
+
},
|
|
330
|
+
};
|
|
331
|
+
const requestB64 = (0, mpp_1.base64urlEncode)(JSON.stringify(requestObj));
|
|
332
|
+
// Generate challenge ID (HMAC-bound in production, random for now)
|
|
333
|
+
const challengeId = `ch_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 10)}`;
|
|
334
|
+
return `Payment id="${challengeId}", realm="pay.asgcard.dev", method="stripe", intent="charge", expires="${expires}", request="${requestB64}"`;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
exports.StripePaymentAdapter = StripePaymentAdapter;
|
|
338
|
+
//# sourceMappingURL=stripe.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stripe.js","sourceRoot":"","sources":["../../../src/adapters/stripe.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/adapters/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.OwsClient = void 0;
|
|
7
|
+
const axios_1 = __importDefault(require("axios"));
|
|
8
|
+
const policy_1 = require("./policy");
|
|
9
|
+
const logger_1 = require("./logger");
|
|
10
|
+
const mpp_1 = require("./mpp");
|
|
11
|
+
/**
|
|
12
|
+
* OwsClient — Dual-protocol autonomous HTTP client for AI agents.
|
|
13
|
+
*
|
|
14
|
+
* Supports BOTH payment protocols:
|
|
15
|
+
* - **x402** (Coinbase/Cloudflare): JSON body challenges + X-PAYMENT header
|
|
16
|
+
* - **MPP** (Stripe/Tempo): WWW-Authenticate challenges + Authorization: Payment header
|
|
17
|
+
*
|
|
18
|
+
* Wraps Axios with an interceptor that automatically detects the protocol,
|
|
19
|
+
* validates spend against PolicyEngine, settles via pluggable adapter,
|
|
20
|
+
* and retries — all without human interaction.
|
|
21
|
+
*
|
|
22
|
+
* @see https://openwallet.sh — Open Wallet Standard specification
|
|
23
|
+
* @see https://x402.org — x402 payment protocol
|
|
24
|
+
* @see https://mpp.dev — Machine Payments Protocol
|
|
25
|
+
* @see https://pay.asgcard.dev — Production ASG Pay infrastructure
|
|
26
|
+
*/
|
|
27
|
+
class OwsClient {
|
|
28
|
+
api;
|
|
29
|
+
policyEngine;
|
|
30
|
+
adapter;
|
|
31
|
+
log;
|
|
32
|
+
constructor(options) {
|
|
33
|
+
this.log = options.logger ?? logger_1.noopLogger;
|
|
34
|
+
this.policyEngine = new policy_1.PolicyEngine(options.policy, this.log);
|
|
35
|
+
this.adapter = options.adapter;
|
|
36
|
+
this.api = axios_1.default.create({
|
|
37
|
+
baseURL: options.baseURL,
|
|
38
|
+
});
|
|
39
|
+
// ── Axios Interceptor: Dual-Protocol 402 Handler ────────────────
|
|
40
|
+
this.api.interceptors.response.use((response) => response, async (error) => {
|
|
41
|
+
if (error.response && error.response.status === 402) {
|
|
42
|
+
return this.handle402(error);
|
|
43
|
+
}
|
|
44
|
+
return Promise.reject(error);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Handle a 402 Payment Required challenge.
|
|
49
|
+
* Auto-detects protocol (MPP vs x402) and routes accordingly.
|
|
50
|
+
*/
|
|
51
|
+
async handle402(error) {
|
|
52
|
+
this.log('[OWS] ⚡ Received 402 Payment Required');
|
|
53
|
+
// Detect protocol from response
|
|
54
|
+
const headers = error.response.headers;
|
|
55
|
+
const body = error.response.data;
|
|
56
|
+
const protocol = (0, mpp_1.detectProtocol)(headers, body);
|
|
57
|
+
this.log(`[OWS] 🔍 Protocol detected: ${protocol}`);
|
|
58
|
+
switch (protocol) {
|
|
59
|
+
case 'mpp':
|
|
60
|
+
return this.handleMpp402(error, headers);
|
|
61
|
+
case 'x402':
|
|
62
|
+
return this.handleX402(error);
|
|
63
|
+
default:
|
|
64
|
+
throw new Error('Unrecognisable 402 challenge — neither x402 nor MPP.');
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ─── MPP Protocol Handler ─────────────────────────────────────────
|
|
68
|
+
/**
|
|
69
|
+
* Handle an MPP 402: parse WWW-Authenticate, create credential, retry.
|
|
70
|
+
*/
|
|
71
|
+
async handleMpp402(error, headers) {
|
|
72
|
+
this.log('[OWS/MPP] 🏛️ Processing MPP challenge');
|
|
73
|
+
// ── Step 1: Parse challenges from WWW-Authenticate headers ────
|
|
74
|
+
const challenges = (0, mpp_1.extractMppChallenges)(headers);
|
|
75
|
+
if (challenges.length === 0) {
|
|
76
|
+
throw new Error('MPP 402 but no valid Payment challenges in WWW-Authenticate header.');
|
|
77
|
+
}
|
|
78
|
+
// Select the best challenge (prefer stripe if adapter is Stripe, else first)
|
|
79
|
+
const challenge = this.selectChallenge(challenges);
|
|
80
|
+
this.log(`[OWS/MPP] 📋 Challenge: method=${challenge.method}, id=${challenge.id}, realm=${challenge.realm}`);
|
|
81
|
+
// ── Step 2: Check expiration ──────────────────────────────────
|
|
82
|
+
if (challenge.expires) {
|
|
83
|
+
const expiresAt = new Date(challenge.expires).getTime();
|
|
84
|
+
if (Date.now() > expiresAt) {
|
|
85
|
+
this.log('[OWS/MPP] 🛑 Challenge expired');
|
|
86
|
+
return Promise.reject(error);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
// ── Step 3: Decode request and extract amount ─────────────────
|
|
90
|
+
const request = (0, mpp_1.decodeChallengeRequest)(challenge);
|
|
91
|
+
const requestedUsdAmount = this.extractMppUsdAmount(request);
|
|
92
|
+
this.log(`[OWS/MPP] 💰 Amount: $${requestedUsdAmount} ${request.currency?.toUpperCase() || 'USD'}`);
|
|
93
|
+
// ── Step 4: Policy gate ───────────────────────────────────────
|
|
94
|
+
if (!this.policyEngine.checkPolicy(requestedUsdAmount, request.recipient)) {
|
|
95
|
+
this.log('[OWS/MPP] 🛑 REJECTED by PolicyEngine');
|
|
96
|
+
return Promise.reject(error);
|
|
97
|
+
}
|
|
98
|
+
this.log('[OWS/MPP] ✅ Policy passed — settling via adapter…');
|
|
99
|
+
// ── Step 5: Pass challenge to adapter (if Stripe) ─────────────
|
|
100
|
+
if (this.isStripeAdapter(this.adapter)) {
|
|
101
|
+
this.adapter.setMppChallenge(challenge);
|
|
102
|
+
}
|
|
103
|
+
// ── Step 6: Settle via adapter ────────────────────────────────
|
|
104
|
+
const result = await this.adapter.pay(request.recipient || challenge.realm, request.amount, challenge.method);
|
|
105
|
+
if (!result) {
|
|
106
|
+
this.log('[OWS/MPP] ❌ Settlement failed');
|
|
107
|
+
return Promise.reject(error);
|
|
108
|
+
}
|
|
109
|
+
// ── Step 7: Record spend ──────────────────────────────────────
|
|
110
|
+
this.policyEngine.recordSpend(requestedUsdAmount);
|
|
111
|
+
// ── Step 8: Build Authorization header ────────────────────────
|
|
112
|
+
// For Stripe adapter: result IS the base64url credential
|
|
113
|
+
// For others: wrap result as a generic credential
|
|
114
|
+
let authHeader;
|
|
115
|
+
if (this.isStripeAdapter(this.adapter) && this.adapter.getLastCredential()) {
|
|
116
|
+
authHeader = `Payment ${this.adapter.getLastCredential()}`;
|
|
117
|
+
}
|
|
118
|
+
else {
|
|
119
|
+
// Generic credential for non-Stripe MPP methods
|
|
120
|
+
const credentialPayload = {
|
|
121
|
+
challenge: {
|
|
122
|
+
id: challenge.id,
|
|
123
|
+
realm: challenge.realm,
|
|
124
|
+
method: challenge.method,
|
|
125
|
+
intent: challenge.intent,
|
|
126
|
+
request: challenge.request,
|
|
127
|
+
...(challenge.expires ? { expires: challenge.expires } : {}),
|
|
128
|
+
},
|
|
129
|
+
source: this.adapter.getAddress(),
|
|
130
|
+
payload: { transaction: result },
|
|
131
|
+
};
|
|
132
|
+
const encoded = Buffer.from(JSON.stringify(credentialPayload))
|
|
133
|
+
.toString('base64')
|
|
134
|
+
.replace(/\+/g, '-')
|
|
135
|
+
.replace(/\//g, '_')
|
|
136
|
+
.replace(/=+$/, '');
|
|
137
|
+
authHeader = `Payment ${encoded}`;
|
|
138
|
+
}
|
|
139
|
+
this.log(`[OWS/MPP] 🔁 Retrying with Authorization: Payment …`);
|
|
140
|
+
// ── Step 9: Retry original request ────────────────────────────
|
|
141
|
+
const originalRequest = error.config;
|
|
142
|
+
if (!originalRequest) {
|
|
143
|
+
throw new Error('Cannot retry — original request config is missing.');
|
|
144
|
+
}
|
|
145
|
+
if (!originalRequest.headers) {
|
|
146
|
+
originalRequest.headers = {};
|
|
147
|
+
}
|
|
148
|
+
originalRequest.headers['Authorization'] = authHeader;
|
|
149
|
+
return this.api.request(originalRequest);
|
|
150
|
+
}
|
|
151
|
+
// ─── x402 Protocol Handler ────────────────────────────────────────
|
|
152
|
+
/**
|
|
153
|
+
* Handle an x402 402: parse JSON body, settle on-chain, retry with X-PAYMENT.
|
|
154
|
+
*/
|
|
155
|
+
async handleX402(error) {
|
|
156
|
+
this.log('[OWS/x402] ⛓️ Processing x402 challenge');
|
|
157
|
+
const challenge = error.response.data;
|
|
158
|
+
// Validate x402 envelope
|
|
159
|
+
if (!challenge.x402Version && !challenge.accepts) {
|
|
160
|
+
throw new Error('Unrecognisable 402 challenge — not x402-compliant.');
|
|
161
|
+
}
|
|
162
|
+
if (!challenge.accepts || challenge.accepts.length === 0) {
|
|
163
|
+
throw new Error('402 challenge has no accepts[] entries.');
|
|
164
|
+
}
|
|
165
|
+
const acceptRules = challenge.accepts[0];
|
|
166
|
+
const { payTo, amount: atomicAmount, network } = acceptRules;
|
|
167
|
+
// ── Extract USD amount ────────────────────────────────────────
|
|
168
|
+
const requestedUsdAmount = this.extractUsdAmount(challenge, acceptRules);
|
|
169
|
+
this.log(`[OWS/x402] 💰 Amount: $${requestedUsdAmount} → ${payTo.slice(0, 10)}…`);
|
|
170
|
+
this.log(`[OWS/x402] ⛓️ Chain: ${this.adapter.chainName} (${this.adapter.caip2Id})`);
|
|
171
|
+
// ── Policy gate ───────────────────────────────────────────────
|
|
172
|
+
if (!this.policyEngine.checkPolicy(requestedUsdAmount, payTo)) {
|
|
173
|
+
this.log('[OWS/x402] 🛑 REJECTED by PolicyEngine');
|
|
174
|
+
return Promise.reject(error);
|
|
175
|
+
}
|
|
176
|
+
this.log('[OWS/x402] ✅ Policy passed — settling on-chain…');
|
|
177
|
+
// ── On-chain settlement ───────────────────────────────────────
|
|
178
|
+
const txHash = await this.adapter.pay(payTo, atomicAmount, network);
|
|
179
|
+
if (!txHash) {
|
|
180
|
+
this.log('[OWS/x402] ❌ Settlement failed');
|
|
181
|
+
return Promise.reject(error);
|
|
182
|
+
}
|
|
183
|
+
// ── Record spend ──────────────────────────────────────────────
|
|
184
|
+
this.policyEngine.recordSpend(requestedUsdAmount);
|
|
185
|
+
// ── Build X-PAYMENT proof ─────────────────────────────────────
|
|
186
|
+
const paymentPayload = {
|
|
187
|
+
x402Version: challenge.x402Version || 2,
|
|
188
|
+
accepted: {
|
|
189
|
+
scheme: acceptRules.scheme,
|
|
190
|
+
network: acceptRules.network,
|
|
191
|
+
amount: acceptRules.amount,
|
|
192
|
+
payTo: acceptRules.payTo,
|
|
193
|
+
asset: acceptRules.asset,
|
|
194
|
+
},
|
|
195
|
+
payload: {
|
|
196
|
+
transaction: txHash,
|
|
197
|
+
chain: this.adapter.caip2Id,
|
|
198
|
+
},
|
|
199
|
+
};
|
|
200
|
+
const tokenBase64 = Buffer.from(JSON.stringify(paymentPayload)).toString('base64');
|
|
201
|
+
this.log(`[OWS/x402] 🔁 Retrying with X-PAYMENT (tx: ${txHash.slice(0, 16)}…)`);
|
|
202
|
+
// ── Retry original request ────────────────────────────────────
|
|
203
|
+
const originalRequest = error.config;
|
|
204
|
+
if (!originalRequest) {
|
|
205
|
+
throw new Error('Cannot retry — original request config is missing.');
|
|
206
|
+
}
|
|
207
|
+
if (!originalRequest.headers) {
|
|
208
|
+
originalRequest.headers = {};
|
|
209
|
+
}
|
|
210
|
+
originalRequest.headers['X-PAYMENT'] = tokenBase64;
|
|
211
|
+
return this.api.request(originalRequest);
|
|
212
|
+
}
|
|
213
|
+
// ─── MPP Helpers ──────────────────────────────────────────────────
|
|
214
|
+
/**
|
|
215
|
+
* Select the best challenge when multiple are offered.
|
|
216
|
+
* Prefers the method matching the current adapter.
|
|
217
|
+
*/
|
|
218
|
+
selectChallenge(challenges) {
|
|
219
|
+
if (challenges.length === 1)
|
|
220
|
+
return challenges[0];
|
|
221
|
+
// Match adapter type to challenge method
|
|
222
|
+
if (this.isStripeAdapter(this.adapter)) {
|
|
223
|
+
const stripeChallenge = challenges.find((c) => c.method === 'stripe');
|
|
224
|
+
if (stripeChallenge)
|
|
225
|
+
return stripeChallenge;
|
|
226
|
+
}
|
|
227
|
+
// Fallback: first challenge
|
|
228
|
+
return challenges[0];
|
|
229
|
+
}
|
|
230
|
+
/**
|
|
231
|
+
* Extract USD amount from MPP request object.
|
|
232
|
+
*/
|
|
233
|
+
extractMppUsdAmount(request) {
|
|
234
|
+
const amount = parseFloat(request.amount);
|
|
235
|
+
if (isNaN(amount) || amount <= 0)
|
|
236
|
+
return 0.01;
|
|
237
|
+
// If currency is USD and has decimals info, convert
|
|
238
|
+
const decimals = request.decimals ?? 2;
|
|
239
|
+
return amount / Math.pow(10, decimals);
|
|
240
|
+
}
|
|
241
|
+
// ─── x402 Helpers ─────────────────────────────────────────────────
|
|
242
|
+
/**
|
|
243
|
+
* Extract USD amount from x402 challenge.
|
|
244
|
+
* Priority: resource.usdAmount → accepts[].maxAmountRequired → heuristic from description.
|
|
245
|
+
*/
|
|
246
|
+
extractUsdAmount(challenge, acceptRules) {
|
|
247
|
+
// 1. Explicit USD amount field (best)
|
|
248
|
+
if (challenge.resource?.usdAmount && challenge.resource.usdAmount > 0) {
|
|
249
|
+
return challenge.resource.usdAmount;
|
|
250
|
+
}
|
|
251
|
+
// 2. maxAmountRequired in accepts (x402 v2)
|
|
252
|
+
if (acceptRules.maxAmountRequired) {
|
|
253
|
+
const parsed = parseFloat(acceptRules.maxAmountRequired);
|
|
254
|
+
if (!isNaN(parsed) && parsed > 0)
|
|
255
|
+
return parsed;
|
|
256
|
+
}
|
|
257
|
+
// 3. Fallback: parse from description text (last resort)
|
|
258
|
+
const desc = challenge.resource?.description || '';
|
|
259
|
+
const match = desc.match(/\$(\d+(?:\.\d+)?)/);
|
|
260
|
+
if (match) {
|
|
261
|
+
return parseFloat(match[1]);
|
|
262
|
+
}
|
|
263
|
+
// 4. Last resort: use atomic amount as-is
|
|
264
|
+
this.log('[OWS] ⚠️ Could not determine USD amount — using atomic fallback');
|
|
265
|
+
return parseFloat(atomicToUsd(acceptRules.amount, acceptRules.asset));
|
|
266
|
+
}
|
|
267
|
+
// ─── Type Guards ──────────────────────────────────────────────────
|
|
268
|
+
isStripeAdapter(adapter) {
|
|
269
|
+
return adapter.chainName === 'Stripe MPP';
|
|
270
|
+
}
|
|
271
|
+
// ─── Public API ───────────────────────────────────────────────────
|
|
272
|
+
/**
|
|
273
|
+
* High-level helper for AI agents.
|
|
274
|
+
* The agent simply calls performTask() — the interceptor handles
|
|
275
|
+
* everything else (payment, policy, retry) transparently.
|
|
276
|
+
*/
|
|
277
|
+
async performTask(endpoint, data) {
|
|
278
|
+
this.log(`[Agent] 🧠 Sending task to ${endpoint}`);
|
|
279
|
+
const res = await this.api.post(endpoint, data);
|
|
280
|
+
this.log(`[Agent] ✅ Task completed`);
|
|
281
|
+
return res.data;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* GET request with autonomous 402 handling.
|
|
285
|
+
*/
|
|
286
|
+
async get(endpoint) {
|
|
287
|
+
const res = await this.api.get(endpoint);
|
|
288
|
+
return res.data;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
exports.OwsClient = OwsClient;
|
|
292
|
+
/**
|
|
293
|
+
* Convert atomic units to approximate USD (best-effort).
|
|
294
|
+
* Used only as a last-resort fallback.
|
|
295
|
+
*/
|
|
296
|
+
function atomicToUsd(amount, asset) {
|
|
297
|
+
const n = BigInt(amount);
|
|
298
|
+
switch (asset.toUpperCase()) {
|
|
299
|
+
case 'USDC':
|
|
300
|
+
return (Number(n) / 1e6).toFixed(2);
|
|
301
|
+
case 'ETH':
|
|
302
|
+
return (Number(n) / 1e18 * 3000).toFixed(2);
|
|
303
|
+
case 'XLM':
|
|
304
|
+
return (Number(n) / 1e7 * 0.1).toFixed(2);
|
|
305
|
+
default:
|
|
306
|
+
return '0.01';
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
//# sourceMappingURL=client.js.map
|