@envarai/envarpay 0.2.0-alpha.2 → 0.2.0-alpha.20

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.
Files changed (85) hide show
  1. package/README.md +18 -56
  2. package/dist/commerce/agent-setup.d.ts +19 -0
  3. package/dist/commerce/agent-setup.js +210 -0
  4. package/dist/commerce/alerts.d.ts +27 -0
  5. package/dist/commerce/alerts.js +71 -0
  6. package/dist/commerce/card.js +3 -3
  7. package/dist/commerce/cli.js +91 -119
  8. package/dist/commerce/config.d.ts +3 -2
  9. package/dist/commerce/config.js +19 -77
  10. package/dist/commerce/configure.d.ts +2 -0
  11. package/dist/commerce/configure.js +78 -0
  12. package/dist/commerce/discovery.d.ts +20 -0
  13. package/dist/commerce/discovery.js +162 -0
  14. package/dist/commerce/envar-balance.d.ts +25 -0
  15. package/dist/commerce/envar-balance.js +65 -0
  16. package/dist/commerce/envar-runtime.d.ts +3 -1
  17. package/dist/commerce/envar-runtime.js +28 -1
  18. package/dist/commerce/envar.d.ts +13 -31
  19. package/dist/commerce/envar.js +35 -83
  20. package/dist/commerce/external-agent.d.ts +26 -0
  21. package/dist/commerce/external-agent.js +129 -0
  22. package/dist/commerce/external-events.d.ts +76 -0
  23. package/dist/commerce/external-events.js +273 -0
  24. package/dist/commerce/external-mcp.d.ts +10 -0
  25. package/dist/commerce/external-mcp.js +199 -0
  26. package/dist/commerce/external-oauth.d.ts +12 -0
  27. package/dist/commerce/external-oauth.js +230 -0
  28. package/dist/commerce/external-runtime.d.ts +25 -0
  29. package/dist/commerce/external-runtime.js +244 -0
  30. package/dist/commerce/index.d.ts +1 -0
  31. package/dist/commerce/index.js +1 -0
  32. package/dist/commerce/managed.d.ts +3 -0
  33. package/dist/commerce/managed.js +143 -0
  34. package/dist/commerce/native-inventory.d.ts +9 -0
  35. package/dist/commerce/native-inventory.js +75 -0
  36. package/dist/commerce/native-skills.d.ts +32 -0
  37. package/dist/commerce/native-skills.js +174 -0
  38. package/dist/commerce/openclaw-setup.d.ts +28 -0
  39. package/dist/commerce/openclaw-setup.js +43 -0
  40. package/dist/commerce/operations.d.ts +8 -3
  41. package/dist/commerce/operations.js +21 -18
  42. package/dist/commerce/pricing.d.ts +0 -1
  43. package/dist/commerce/pricing.js +3 -5
  44. package/dist/commerce/runtime.d.ts +4 -2
  45. package/dist/commerce/runtime.js +4 -2
  46. package/dist/commerce/server.d.ts +3 -2
  47. package/dist/commerce/server.js +26 -10
  48. package/dist/commerce/setup.d.ts +25 -0
  49. package/dist/commerce/setup.js +379 -0
  50. package/dist/commerce/skill-gate.d.ts +54 -0
  51. package/dist/commerce/skill-gate.js +153 -0
  52. package/dist/commerce/store.d.ts +11 -1
  53. package/dist/commerce/store.js +36 -2
  54. package/dist/commerce/types.d.ts +6 -57
  55. package/examples/envar-integration-policy.json +1 -9
  56. package/examples/hermes-setup-probe.py +109 -0
  57. package/examples/hermes-skill-inventory.py +108 -0
  58. package/examples/native-skill-runner.py +197 -0
  59. package/examples/seller.json +143 -26
  60. package/package.json +5 -16
  61. package/schemas/envarpay.config.schema.json +28 -87
  62. package/dist/commerce/buyer-store.d.ts +0 -101
  63. package/dist/commerce/buyer-store.js +0 -168
  64. package/dist/commerce/buyer.d.ts +0 -132
  65. package/dist/commerce/buyer.js +0 -755
  66. package/dist/commerce/chain.d.ts +0 -24
  67. package/dist/commerce/chain.js +0 -146
  68. package/dist/commerce/client-runtime.d.ts +0 -8
  69. package/dist/commerce/client-runtime.js +0 -7
  70. package/dist/commerce/management.d.ts +0 -14
  71. package/dist/commerce/management.js +0 -138
  72. package/dist/commerce/mpp-client.d.ts +0 -59
  73. package/dist/commerce/mpp-client.js +0 -24
  74. package/dist/commerce/mpp-runtime.d.ts +0 -3
  75. package/dist/commerce/mpp-runtime.js +0 -3
  76. package/dist/commerce/mpp.d.ts +0 -53
  77. package/dist/commerce/mpp.js +0 -163
  78. package/dist/commerce/peer-proxy.d.ts +0 -44
  79. package/dist/commerce/peer-proxy.js +0 -673
  80. package/dist/commerce/stripe-provider.d.ts +0 -9
  81. package/dist/commerce/stripe-provider.js +0 -97
  82. package/dist/commerce/x402.d.ts +0 -37
  83. package/dist/commerce/x402.js +0 -174
  84. package/examples/buyer-policy.json +0 -18
  85. package/schemas/buyer-policy.schema.json +0 -253
@@ -1,163 +0,0 @@
1
- import { Mppx } from 'mppx/server/core';
2
- import { charge } from 'mppx/stripe/server';
3
- import { Challenge, Credential, Receipt } from 'mppx';
4
- import { CommerceError } from './types.js';
5
- import { digest } from './config.js';
6
- /** Native MPP Stripe charge; only a verified PSP success may queue a task. */
7
- export class MppGate {
8
- options;
9
- constructor(options) {
10
- this.options = options;
11
- if (Buffer.byteLength(options.hmacSecret) < 32)
12
- throw new CommerceError('mpp_hmac_key', 'MPP challenge key must contain at least 32 bytes');
13
- }
14
- merchantRecipient(accountRef) { return this.options.providers[accountRef]?.merchantProfile; }
15
- async check() {
16
- for (const provider of Object.values(this.options.providers))
17
- await provider.assertReady();
18
- }
19
- provider(order) {
20
- const profile = order.quote.paymentProfile;
21
- if (profile?.adapter !== 'mpp')
22
- throw new CommerceError('payment_method_unavailable', 'Not an MPP offer');
23
- const provider = this.options.providers[profile.accountRef];
24
- if (!provider || provider.merchantProfile !== order.quote.recipient)
25
- throw new CommerceError('merchant_not_ready', 'Configured merchant must match original quote');
26
- return provider;
27
- }
28
- optionsFor(order, provider) {
29
- const amount = BigInt(order.quote.amount);
30
- if (amount < 50n || amount > 99999999n)
31
- throw new CommerceError('stripe_amount', 'Stripe USD purchase must be 50–99999999 cents');
32
- return {
33
- amount: `${amount / 100n}.${(amount % 100n).toString().padStart(2, '0')}`,
34
- currency: 'usd', decimals: 2, externalId: order.quote.quoteId,
35
- recipient: provider.merchantProfile, networkId: provider.merchantProfile,
36
- expires: order.quote.expiresAt,
37
- scope: `service:${order.serviceRevision}/offer:${order.offerId}`,
38
- meta: {
39
- caller: digest(order.caller), inputDigest: order.inputDigest, termsDigest: order.quote.termsDigest,
40
- messageId: order.messageId, quoteId: order.quote.quoteId, serviceId: order.quote.serviceId, serviceRevision: String(order.quote.serviceRevision),
41
- offerId: order.offerId, amount: order.quote.amount, currency: 'usd', recipient: provider.merchantProfile, expiresAt: order.quote.expiresAt,
42
- },
43
- metadata: { envarpay_order: order.id, envarpay_quote: order.quote.quoteId, envarpay_terms: order.quote.termsDigest },
44
- };
45
- }
46
- runtime(create) {
47
- return Mppx.create({
48
- methods: [charge({ client: { paymentIntents: { create } }, paymentMethodTypes: ['card'] })],
49
- realm: new URL(this.options.origin).host, secretKey: this.options.hmacSecret, requiresAuth: true,
50
- });
51
- }
52
- bound(intent, order, provider) {
53
- return intent.livemode === (provider.mode === 'live') && Number.isSafeInteger(intent.amount) && String(intent.amount) === order.quote.amount &&
54
- intent.currency === 'usd' && intent.metadata?.envarpay_order === order.id && intent.metadata.envarpay_quote === order.quote.quoteId && intent.metadata.envarpay_terms === order.quote.termsDigest;
55
- }
56
- verified(intent, order, provider) {
57
- return this.bound(intent, order, provider) && intent.status === 'succeeded' && intent.livemode === (provider.mode === 'live') &&
58
- Number.isSafeInteger(intent.amount) && String(intent.amount) === order.quote.amount &&
59
- Number.isSafeInteger(intent.amount_received) && String(intent.amount_received) === order.quote.amount &&
60
- intent.currency === 'usd' && intent.metadata?.envarpay_order === order.id &&
61
- intent.metadata.envarpay_quote === order.quote.quoteId && intent.metadata.envarpay_terms === order.quote.termsDigest;
62
- }
63
- receipt(intent, order, provider) {
64
- return {
65
- method: 'stripe', status: 'success', reference: intent.id, externalId: order.quote.quoteId,
66
- timestamp: new Date().toISOString(), amount: order.quote.amount, currency: 'usd',
67
- merchant: provider.merchantProfile, accountId: provider.accountId, livemode: intent.livemode,
68
- };
69
- }
70
- headers(receipt) {
71
- return { headers: { 'Payment-Receipt': Receipt.serialize(receipt) } };
72
- }
73
- async recover(orderId, caller) {
74
- const order = this.options.store.getOrder(orderId, caller);
75
- if (!order)
76
- throw new CommerceError('order_not_found', 'Original order not found');
77
- const attempt = this.options.store.paymentForOrder(order.id);
78
- if (!attempt)
79
- throw new CommerceError('attempt_not_found', 'No original MPP attempt');
80
- if (attempt.state === 'confirmed' || attempt.state === 'rejected')
81
- return { state: attempt.state, taskId: order.taskId };
82
- const provider = this.provider(order);
83
- const saved = this.options.vault.get(attempt.credentialRef);
84
- if (saved.quoteId !== order.quote.quoteId || saved.accountId !== provider.accountId || saved.mode !== provider.mode)
85
- throw new CommerceError('recovery_conflict', 'Original provider binding changed');
86
- // Read only: Stripe idempotency cache expiry must never cause a second charge.
87
- const reference = attempt.receipt?.reference;
88
- const intent = typeof reference === 'string' && /^pi_[A-Za-z0-9]+$/.test(reference)
89
- ? await provider.retrieve(reference) : await provider.findOriginal(order.id);
90
- if (!intent)
91
- return { state: 'unknown', taskId: order.taskId };
92
- if (this.verified(intent, order, provider)) {
93
- this.options.store.recordSettlement(attempt.id, 'confirmed', this.receipt(intent, order, provider));
94
- return { state: 'confirmed', taskId: order.taskId };
95
- }
96
- // An action-required or declined intent might still change later. Only a
97
- // canceled PSP intent is definitive non-payment; do not release on timeout.
98
- const canceled = this.bound(intent, order, provider) && intent.status === 'canceled' && intent.amount_received === 0;
99
- const state = canceled ? 'rejected' : 'unknown';
100
- this.options.store.recordSettlement(attempt.id, state, { reference: intent.id, providerState: intent.status, livemode: intent.livemode });
101
- return { state, taskId: order.taskId };
102
- }
103
- async handle(request, order) {
104
- const provider = this.provider(order);
105
- const prior = this.options.store.paymentForOrder(order.id);
106
- if (order.paymentState === 'confirmed' && prior?.receipt)
107
- return this.headers(prior.receipt);
108
- if (['settling', 'unknown'].includes(order.paymentState)) {
109
- const recovered = await this.recover(order.id, order.caller);
110
- if (recovered.state === 'confirmed')
111
- return this.headers(this.options.store.paymentForOrder(order.id).receipt);
112
- return { response: Response.json({ error: 'payment_reconciliation_required', state: recovered.state }, { status: 503 }) };
113
- }
114
- if (Date.now() >= Date.parse(order.quote.expiresAt))
115
- return { response: Response.json({ error: 'quote_expired' }, { status: 409 }) };
116
- await provider.assertReady();
117
- const runtime = this.runtime(provider.create.bind(provider));
118
- const options = this.optionsFor(order, provider);
119
- let challenge = this.options.store.paymentChallenge(order.id);
120
- if (!challenge)
121
- challenge = this.options.store.freezePaymentChallenge(order.id, await runtime.challenge.stripe.charge(options));
122
- const unpaid = () => ({ response: new Response('{}', { status: 402, headers: {
123
- 'Content-Type': 'application/json', 'Cache-Control': 'no-store', 'WWW-Authenticate': Challenge.serialize(challenge),
124
- } }) });
125
- const header = request.headers.get('Payment-Authorization');
126
- if (!header)
127
- return unpaid();
128
- if (header.length > 32768)
129
- throw new CommerceError('payment_payload_size', 'Payment credential exceeds limit');
130
- let credential;
131
- try {
132
- credential = Credential.deserialize(header);
133
- }
134
- catch {
135
- throw new CommerceError('invalid_payment', 'Malformed MPP credential');
136
- }
137
- // Equality against a server-persisted native challenge complements the SDK HMAC.
138
- if (Challenge.serialize(credential.challenge) !== Challenge.serialize(challenge) || !Challenge.verify(credential.challenge, { secretKey: this.options.hmacSecret }) ||
139
- credential.payload.externalId !== order.quote.quoteId || !/^spt_[A-Za-z0-9_]+$/.test(credential.payload.spt)) {
140
- throw new CommerceError('quote_mismatch', 'MPP credential does not match the original order');
141
- }
142
- const saved = { quoteId: order.quote.quoteId, accountId: provider.accountId, mode: provider.mode, credential };
143
- const ref = this.options.vault.put(saved);
144
- const attempt = this.options.store.reservePayment(order.id, order.caller, digest({ protocol: 'mpp', spt: credential.payload.spt }), ref);
145
- const wrapped = this.runtime(async (params, requestOptions) => {
146
- const intent = await provider.create(params, requestOptions);
147
- // Preserve original PI even if SDK reports requires_action or replay.
148
- this.options.store.recordSettlement(attempt, 'unknown', { reference: intent.id, providerState: intent.status, livemode: intent.livemode });
149
- return intent;
150
- });
151
- try {
152
- await wrapped.broadcastCredential(credential, { realm: new URL(this.options.origin).host, request: options, scope: options.scope, meta: options.meta });
153
- }
154
- catch {
155
- if (this.options.store.paymentForOrder(order.id)?.state === 'settling')
156
- this.options.store.recordSettlement(attempt, 'unknown', {});
157
- }
158
- const recovered = await this.recover(order.id, order.caller);
159
- if (recovered.state === 'confirmed')
160
- return this.headers(this.options.store.paymentForOrder(order.id).receipt);
161
- return { response: Response.json({ error: 'payment_not_confirmed', state: recovered.state }, { status: 503 }) };
162
- }
163
- }
@@ -1,44 +0,0 @@
1
- import { AgentCard, Task, type SendMessageRequest } from "@a2a-js/sdk";
2
- import { CommerceBuyer } from "./buyer.js";
3
- export interface PeerProxyOptions {
4
- buyer: CommerceBuyer;
5
- buyerCaller: string;
6
- peerId: string;
7
- offerId: string;
8
- origin: string;
9
- stateDirectory: string;
10
- tokens: Readonly<Record<string, string>>;
11
- label?: string;
12
- waitMilliseconds?: number;
13
- /** Explicit opt-in for an isolated Docker/private HTTP network; public deployments require HTTPS. */
14
- allowPrivateHttp?: boolean;
15
- }
16
- /** The proxy token is never a wallet-management token. One proxy is bound to one approved peer/offer. */
17
- export declare class A2APeerProxy {
18
- private readonly options;
19
- readonly origin: string;
20
- readonly card: AgentCard;
21
- private readonly db;
22
- private readonly lock;
23
- private readonly rpc;
24
- private readonly authenticate;
25
- private readonly cardUrl;
26
- private readonly waitMs;
27
- private readonly active;
28
- private stopped;
29
- private closed;
30
- constructor(options: PeerProxyOptions);
31
- private row;
32
- private phase;
33
- private projection;
34
- private exclusive;
35
- private snapshot;
36
- private wait;
37
- send(params: SendMessageRequest, caller: string): Promise<Task>;
38
- read(id: string, caller: string): Promise<Task>;
39
- handle(request: Request): Promise<Response>;
40
- assertListenHost(host: string): void;
41
- stop(): Promise<void>;
42
- close(): void;
43
- }
44
- export declare function listenPeerProxy(proxy: A2APeerProxy, host?: string, port?: number): import("http").Server<typeof import("http").IncomingMessage, typeof import("http").ServerResponse>;