@algopayoracle/oracle-sdk 1.0.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/README.md +397 -0
- package/package.json +60 -0
- package/src/AlgoPayClient.js +298 -0
- package/src/OracleSigner.js +142 -0
- package/src/ProofVerifier.js +132 -0
- package/src/adapters/index.js +4 -0
- package/src/adapters/razorpay.js +177 -0
- package/src/adapters/stripe.js +77 -0
- package/src/apc1.js +112 -0
- package/src/errors.js +96 -0
- package/src/index.d.ts +373 -0
- package/src/index.js +99 -0
- package/src/networks.js +66 -0
- package/src/utils/logger.js +98 -0
- package/src/utils/store.js +119 -0
- package/src/validate.js +105 -0
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlgoPay Oracle SDK — Razorpay Adapter
|
|
3
|
+
*
|
|
4
|
+
* Implements the PaymentAdapter interface for Razorpay.
|
|
5
|
+
* Normalizes Razorpay events into PaymentEvents consumed by AlgoPayClient.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
"use strict";
|
|
9
|
+
|
|
10
|
+
const crypto = require("crypto");
|
|
11
|
+
const https = require("https");
|
|
12
|
+
const { ProviderAuthError, ConfigError } = require("../errors");
|
|
13
|
+
|
|
14
|
+
class RazorpayAdapter {
|
|
15
|
+
/**
|
|
16
|
+
* @param {object} opts
|
|
17
|
+
* @param {string} opts.keySecret - Razorpay key secret
|
|
18
|
+
* @param {string} [opts.keyId] - Razorpay key ID (required for createOrder)
|
|
19
|
+
* @param {object} [opts.orderStore] - shared order store (set/get/consume/delete async interface)
|
|
20
|
+
* @param {string} [opts.defaultAction]
|
|
21
|
+
*/
|
|
22
|
+
constructor({ keySecret, keyId, orderStore, defaultAction = "unlock" } = {}) {
|
|
23
|
+
if (!keySecret) throw new ConfigError("RazorpayAdapter: keySecret is required");
|
|
24
|
+
this.keySecret = keySecret;
|
|
25
|
+
this.keyId = keyId;
|
|
26
|
+
this.orderStore = orderStore || null;
|
|
27
|
+
this.defaultAction = defaultAction;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ── parseWebhook ───────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Parse and verify a Razorpay server-side webhook.
|
|
34
|
+
* Implements PaymentAdapter.parseWebhook.
|
|
35
|
+
* Returns null on any failure (invalid sig, wrong event, bad body).
|
|
36
|
+
*
|
|
37
|
+
* @param {Buffer|string} rawBody
|
|
38
|
+
* @param {string} signature - X-Razorpay-Signature header
|
|
39
|
+
* @returns {import("../index").PaymentEvent|null}
|
|
40
|
+
*/
|
|
41
|
+
parseWebhook(rawBody, signature) {
|
|
42
|
+
if (!signature) return null;
|
|
43
|
+
|
|
44
|
+
const expected = crypto.createHmac("sha256", this.keySecret).update(rawBody).digest();
|
|
45
|
+
const received = Buffer.from(signature, "hex");
|
|
46
|
+
if (expected.length !== received.length) return null;
|
|
47
|
+
if (!crypto.timingSafeEqual(expected, received)) return null;
|
|
48
|
+
|
|
49
|
+
let body;
|
|
50
|
+
try { body = JSON.parse(rawBody.toString()); } catch { return null; }
|
|
51
|
+
|
|
52
|
+
if (body.event !== "payment.captured") return null;
|
|
53
|
+
|
|
54
|
+
const p = body?.payload?.payment?.entity;
|
|
55
|
+
if (!p?.id || !p?.amount) return null;
|
|
56
|
+
|
|
57
|
+
return {
|
|
58
|
+
payment_id: p.id,
|
|
59
|
+
amount: Math.round(p.amount / 100),
|
|
60
|
+
currency: (p.currency || "INR").toUpperCase(),
|
|
61
|
+
action: this.defaultAction,
|
|
62
|
+
provider: "razorpay",
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ── parseClientPayment ────────────────────────────────────────────────────
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Verify a Razorpay client-side payment and return a PaymentEvent.
|
|
70
|
+
* Amount is resolved from the server-side orderStore — never from the request.
|
|
71
|
+
*
|
|
72
|
+
* @param {object} opts
|
|
73
|
+
* @param {string} opts.razorpay_order_id
|
|
74
|
+
* @param {string} opts.razorpay_payment_id
|
|
75
|
+
* @param {string} opts.razorpay_signature
|
|
76
|
+
* @param {string} [opts.action]
|
|
77
|
+
* @throws {ProviderAuthError} on invalid signature or missing order
|
|
78
|
+
* @returns {import("../index").PaymentEvent}
|
|
79
|
+
*/
|
|
80
|
+
async parseClientPayment({ razorpay_order_id, razorpay_payment_id, razorpay_signature, action }) {
|
|
81
|
+
if (!razorpay_order_id || !razorpay_payment_id || !razorpay_signature) {
|
|
82
|
+
throw new ProviderAuthError("razorpay", "missing required payment fields");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const expected = crypto
|
|
86
|
+
.createHmac("sha256", this.keySecret)
|
|
87
|
+
.update(`${razorpay_order_id}|${razorpay_payment_id}`)
|
|
88
|
+
.digest();
|
|
89
|
+
const received = Buffer.from(razorpay_signature, "hex");
|
|
90
|
+
|
|
91
|
+
if (expected.length !== received.length || !crypto.timingSafeEqual(expected, received)) {
|
|
92
|
+
throw new ProviderAuthError("razorpay", "payment signature mismatch");
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!this.orderStore) {
|
|
96
|
+
throw new ConfigError("RazorpayAdapter: orderStore is required for parseClientPayment — amount cannot be trusted from client");
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Amount MUST come from the server-side order record, not the request body
|
|
100
|
+
// This is a synchronous path so we return a promise that resolves the event
|
|
101
|
+
// Callers must await this if orderStore.consume is async
|
|
102
|
+
if (!this.orderStore) {
|
|
103
|
+
throw new ConfigError("RazorpayAdapter: orderStore is required for parseClientPayment — amount cannot be trusted from client");
|
|
104
|
+
}
|
|
105
|
+
const record = await this.orderStore.consume(razorpay_order_id);
|
|
106
|
+
if (!record) {
|
|
107
|
+
throw new ProviderAuthError("razorpay", `order "${razorpay_order_id}" not found or already used — call createOrder first`);
|
|
108
|
+
}
|
|
109
|
+
return {
|
|
110
|
+
payment_id: razorpay_payment_id,
|
|
111
|
+
amount: record.amount,
|
|
112
|
+
currency: record.currency,
|
|
113
|
+
action: action || this.defaultAction,
|
|
114
|
+
provider: "razorpay",
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
// ── createOrder ───────────────────────────────────────────────────────────
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Create a Razorpay order and store the authoritative amount server-side.
|
|
123
|
+
*
|
|
124
|
+
* @param {object} opts
|
|
125
|
+
* @param {number} opts.amount - integer, in rupees
|
|
126
|
+
* @param {string} [opts.currency]
|
|
127
|
+
* @returns {Promise<{ order_id, amount, currency, key_id }>}
|
|
128
|
+
*/
|
|
129
|
+
async createOrder({ amount, currency = "INR" }) {
|
|
130
|
+
if (!this.keyId) throw new ConfigError("RazorpayAdapter: keyId is required for createOrder");
|
|
131
|
+
if (!Number.isInteger(amount) || amount <= 0) throw new ConfigError("RazorpayAdapter: amount must be a positive integer");
|
|
132
|
+
|
|
133
|
+
const body = JSON.stringify({
|
|
134
|
+
amount: amount * 100,
|
|
135
|
+
currency: currency.toUpperCase(),
|
|
136
|
+
receipt: "algopay_" + Date.now(),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const auth = Buffer.from(`${this.keyId}:${this.keySecret}`).toString("base64");
|
|
140
|
+
|
|
141
|
+
const order = await new Promise((resolve, reject) => {
|
|
142
|
+
const req = https.request({
|
|
143
|
+
hostname: "api.razorpay.com",
|
|
144
|
+
path: "/v1/orders",
|
|
145
|
+
method: "POST",
|
|
146
|
+
headers: { "Content-Type": "application/json", Authorization: `Basic ${auth}` },
|
|
147
|
+
}, res => {
|
|
148
|
+
let data = "";
|
|
149
|
+
res.on("data", c => (data += c));
|
|
150
|
+
res.on("end", () => {
|
|
151
|
+
try { resolve(JSON.parse(data)); }
|
|
152
|
+
catch { reject(new ConfigError("RazorpayAdapter: failed to parse order creation response")); }
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
req.on("error", err => reject(new ConfigError(`RazorpayAdapter: order request failed — ${err.message}`)));
|
|
156
|
+
req.write(body);
|
|
157
|
+
req.end();
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (!order.id) {
|
|
161
|
+
throw new ProviderAuthError("razorpay", `order creation failed: ${JSON.stringify(order)}`);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (this.orderStore) {
|
|
165
|
+
await this.orderStore.set(order.id, { amount, currency: currency.toUpperCase() });
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
order_id: order.id,
|
|
170
|
+
amount,
|
|
171
|
+
currency: currency.toUpperCase(),
|
|
172
|
+
key_id: this.keyId,
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
module.exports = { RazorpayAdapter };
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlgoPay Oracle SDK — Stripe Adapter
|
|
3
|
+
*
|
|
4
|
+
* Bridges Stripe payment events to AlgoPay PaymentEvents.
|
|
5
|
+
* The credential layer (APC-1 / oracle signing) is provider-agnostic.
|
|
6
|
+
* This adapter is the provider-specific translation layer for Stripe.
|
|
7
|
+
*
|
|
8
|
+
* Requires the 'stripe' npm package:
|
|
9
|
+
* npm install stripe
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* const adapter = new StripeAdapter({ webhookSecret: process.env.STRIPE_WEBHOOK_SECRET });
|
|
13
|
+
* const event = adapter.parseWebhook(req.rawBody, req.headers["stripe-signature"]);
|
|
14
|
+
* if (!event) return res.status(401).end();
|
|
15
|
+
* const result = await client.verifyAndCommit(event);
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
const { ProviderAuthError, ConfigError } = require("../errors");
|
|
19
|
+
|
|
20
|
+
class StripeAdapter {
|
|
21
|
+
/**
|
|
22
|
+
* @param {object} opts
|
|
23
|
+
* @param {string} opts.webhookSecret - Stripe webhook signing secret (whsec_...)
|
|
24
|
+
* @param {string} [opts.secretKey] - Stripe secret key (sk_test_... or sk_live_...)
|
|
25
|
+
* @param {string} [opts.defaultAction]
|
|
26
|
+
*/
|
|
27
|
+
constructor({ webhookSecret, secretKey, defaultAction = "unlock" } = {}) {
|
|
28
|
+
if (!webhookSecret) throw new ConfigError("StripeAdapter: webhookSecret is required");
|
|
29
|
+
this.webhookSecret = webhookSecret;
|
|
30
|
+
this.defaultAction = defaultAction;
|
|
31
|
+
|
|
32
|
+
// Lazy-load stripe — not a hard SDK dependency
|
|
33
|
+
try {
|
|
34
|
+
const Stripe = require("stripe");
|
|
35
|
+
this.stripe = new Stripe(secretKey || process.env.STRIPE_SECRET_KEY || "");
|
|
36
|
+
} catch {
|
|
37
|
+
this.stripe = null;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Parse and verify a Stripe webhook event.
|
|
43
|
+
* Stripe's SDK uses a timing-safe comparison internally.
|
|
44
|
+
* Returns null on non-payment events, throws ProviderAuthError on bad sig.
|
|
45
|
+
*
|
|
46
|
+
* @param {Buffer} rawBody - raw request body (Buffer, not parsed)
|
|
47
|
+
* @param {string} signature - stripe-signature header value
|
|
48
|
+
* @returns {object|null} PaymentEvent or null
|
|
49
|
+
*/
|
|
50
|
+
parseWebhook(rawBody, signature) {
|
|
51
|
+
if (!this.stripe) throw new ConfigError("StripeAdapter: install 'stripe' npm package");
|
|
52
|
+
|
|
53
|
+
let event;
|
|
54
|
+
try {
|
|
55
|
+
// Stripe constructEvent uses timing-safe HMAC internally
|
|
56
|
+
event = this.stripe.webhooks.constructEvent(rawBody, signature, this.webhookSecret);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
throw new ProviderAuthError("stripe", e.message);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Only handle successful payment intents
|
|
62
|
+
if (event.type !== "payment_intent.succeeded") return null;
|
|
63
|
+
|
|
64
|
+
const intent = event.data.object;
|
|
65
|
+
if (!intent?.id || !intent?.amount) return null;
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
payment_id: intent.id,
|
|
69
|
+
amount: Math.round(intent.amount / 100), // cents → base unit
|
|
70
|
+
currency: (intent.currency || "usd").toUpperCase(),
|
|
71
|
+
action: this.defaultAction,
|
|
72
|
+
provider: "stripe",
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
module.exports = { StripeAdapter };
|
package/src/apc1.js
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlgoPay Credential Standard v1 (APC-1)
|
|
3
|
+
*
|
|
4
|
+
* APC-1 is the standardized proof format produced by AlgoPay Oracle.
|
|
5
|
+
* It is provider-agnostic: the same credential format is produced regardless
|
|
6
|
+
* of whether the payment came from Razorpay, Stripe, UPI, or any other gateway.
|
|
7
|
+
*
|
|
8
|
+
* canonical_id is the replay key used on-chain (provider:payment_id).
|
|
9
|
+
* It must appear in the credential so verifiers can reconstruct the signed message.
|
|
10
|
+
*
|
|
11
|
+
* Schema (v1):
|
|
12
|
+
* apc "1" — format version
|
|
13
|
+
* payment_id string — original provider-issued ID (display only)
|
|
14
|
+
* canonical_id string — namespaced replay key: "provider:payment_id"
|
|
15
|
+
* amount integer — fiat amount in currency base units
|
|
16
|
+
* currency string — ISO 4217 (INR, USD, EUR...)
|
|
17
|
+
* action string — intended Web3 action
|
|
18
|
+
* timestamp integer — unix seconds when oracle signed
|
|
19
|
+
* oracle_address string — Algorand address of signing oracle
|
|
20
|
+
* signature string — base64 Ed25519 signature over canonical message
|
|
21
|
+
* chain "algorand"
|
|
22
|
+
* network "localnet"|"testnet"|"mainnet"
|
|
23
|
+
* app_id integer|null — deployed AlgoPayOracle App ID
|
|
24
|
+
* provider string — payment rail label
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
"use strict";
|
|
28
|
+
|
|
29
|
+
const APC_VERSION = "1";
|
|
30
|
+
const SUPPORTED_APC = new Set(["1"]);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Wrap an internal signed proof as an APC-1 credential.
|
|
34
|
+
* canonical_id is always included — it is the on-chain replay key.
|
|
35
|
+
*
|
|
36
|
+
* @param {object} proof - output of OracleSigner.sign()
|
|
37
|
+
* @param {object} [meta] - { network, appId, provider }
|
|
38
|
+
* @returns {object} APC-1 credential
|
|
39
|
+
*/
|
|
40
|
+
function toAPC1(proof, { network = "testnet", appId = null, provider } = {}) {
|
|
41
|
+
return {
|
|
42
|
+
apc: APC_VERSION,
|
|
43
|
+
payment_id: proof.payment_id,
|
|
44
|
+
canonical_id: proof.canonical_id, // the signed replay key — MUST be present
|
|
45
|
+
amount: proof.amount,
|
|
46
|
+
currency: proof.currency,
|
|
47
|
+
action: proof.action,
|
|
48
|
+
timestamp: proof.timestamp,
|
|
49
|
+
oracle_address: proof.oracle_address,
|
|
50
|
+
signature: proof.signature,
|
|
51
|
+
chain: "algorand",
|
|
52
|
+
network,
|
|
53
|
+
app_id: appId ?? null,
|
|
54
|
+
provider: provider ?? proof.provider ?? "unknown",
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Validate APC-1 credential structure and field types.
|
|
60
|
+
* Does NOT verify the cryptographic signature — use ProofVerifier for that.
|
|
61
|
+
*
|
|
62
|
+
* @param {object} cred
|
|
63
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
64
|
+
*/
|
|
65
|
+
function validateAPC1Structure(cred) {
|
|
66
|
+
const errors = [];
|
|
67
|
+
|
|
68
|
+
if (!SUPPORTED_APC.has(String(cred.apc))) errors.push(`unsupported apc version: "${cred.apc}" (supported: ${[...SUPPORTED_APC].join(", ")})`);
|
|
69
|
+
if (!cred.payment_id || typeof cred.payment_id !== "string")
|
|
70
|
+
errors.push("payment_id must be a non-empty string");
|
|
71
|
+
if (!cred.canonical_id || typeof cred.canonical_id !== "string")
|
|
72
|
+
errors.push("canonical_id must be a non-empty string");
|
|
73
|
+
if (!Number.isInteger(cred.amount) || cred.amount <= 0)
|
|
74
|
+
errors.push("amount must be a positive integer");
|
|
75
|
+
if (typeof cred.currency !== "string" || cred.currency.length !== 3)
|
|
76
|
+
errors.push("currency must be a 3-character ISO code");
|
|
77
|
+
if (!cred.action || typeof cred.action !== "string")
|
|
78
|
+
errors.push("action must be a non-empty string");
|
|
79
|
+
if (!Number.isInteger(cred.timestamp)) errors.push("timestamp must be an integer");
|
|
80
|
+
if (typeof cred.oracle_address !== "string" || cred.oracle_address.length !== 58)
|
|
81
|
+
errors.push("oracle_address must be a valid Algorand address (58 chars)");
|
|
82
|
+
if (!cred.signature || typeof cred.signature !== "string")
|
|
83
|
+
errors.push("signature must be a base64 string");
|
|
84
|
+
if (cred.chain !== "algorand") errors.push("chain must be 'algorand'");
|
|
85
|
+
if (!["localnet", "testnet", "mainnet"].includes(cred.network))
|
|
86
|
+
errors.push("network must be localnet, testnet, or mainnet");
|
|
87
|
+
|
|
88
|
+
return { valid: errors.length === 0, errors };
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Check if a verifier supports this APC-1 version.
|
|
93
|
+
* Allows callers to gate on version before attempting verification.
|
|
94
|
+
*
|
|
95
|
+
* @param {object} cred
|
|
96
|
+
* @returns {boolean}
|
|
97
|
+
*/
|
|
98
|
+
function isSupportedVersion(cred) {
|
|
99
|
+
return SUPPORTED_APC.has(String(cred?.apc));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Check if an APC-1 proof is expired.
|
|
104
|
+
* @param {object} cred
|
|
105
|
+
* @param {number} [maxAgeSecs] - default 300 (5 min)
|
|
106
|
+
*/
|
|
107
|
+
function isExpired(cred, maxAgeSecs = 300) {
|
|
108
|
+
const now = Math.floor(Date.now() / 1000);
|
|
109
|
+
return now - cred.timestamp > maxAgeSecs;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
module.exports = { APC_VERSION, SUPPORTED_APC, toAPC1, validateAPC1Structure, isSupportedVersion, isExpired };
|
package/src/errors.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AlgoPay Oracle SDK — Error Classes
|
|
3
|
+
*
|
|
4
|
+
* All SDK errors extend AlgoPayError so callers can catch by base type.
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* const { AlgoPayError, ProofExpiredError } = require("@algopayoracle/oracle-sdk/errors");
|
|
8
|
+
* try { await client.verifyAndCommit(...) }
|
|
9
|
+
* catch (e) {
|
|
10
|
+
* if (e instanceof ProofExpiredError) { ... }
|
|
11
|
+
* if (e instanceof OracleNotRegisteredError) { ... }
|
|
12
|
+
* }
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
class AlgoPayError extends Error {
|
|
16
|
+
constructor(message, code) {
|
|
17
|
+
super(message);
|
|
18
|
+
this.name = "AlgoPayError";
|
|
19
|
+
this.code = code || "ALGOPAY_ERROR";
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Amount is below the contract's MIN_AMOUNT threshold */
|
|
24
|
+
class InsufficientAmountError extends AlgoPayError {
|
|
25
|
+
constructor(amount, minAmount) {
|
|
26
|
+
super(`Amount ${amount} is below minimum ${minAmount}`, "INSUFFICIENT_AMOUNT");
|
|
27
|
+
this.name = "InsufficientAmountError";
|
|
28
|
+
this.amount = amount;
|
|
29
|
+
this.minAmount = minAmount;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** Proof timestamp is older than PROOF_VALIDITY_SECS */
|
|
34
|
+
class ProofExpiredError extends AlgoPayError {
|
|
35
|
+
constructor(timestamp, now) {
|
|
36
|
+
const age = now - timestamp;
|
|
37
|
+
super(`Proof expired — signed ${age}s ago (max 300s)`, "PROOF_EXPIRED");
|
|
38
|
+
this.name = "ProofExpiredError";
|
|
39
|
+
this.timestamp = timestamp;
|
|
40
|
+
this.age = age;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** oracle_pubkey is not in the contract's oracle registry */
|
|
45
|
+
class OracleNotRegisteredError extends AlgoPayError {
|
|
46
|
+
constructor(address) {
|
|
47
|
+
super(`Oracle ${address} is not registered in the contract`, "ORACLE_NOT_REGISTERED");
|
|
48
|
+
this.name = "OracleNotRegisteredError";
|
|
49
|
+
this.address = address;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** payment_id has already been verified (replay attempt) */
|
|
54
|
+
class ReplayError extends AlgoPayError {
|
|
55
|
+
constructor(paymentId) {
|
|
56
|
+
super(`payment_id "${paymentId}" has already been processed`, "REPLAY_DETECTED");
|
|
57
|
+
this.name = "ReplayError";
|
|
58
|
+
this.paymentId = paymentId;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Ed25519 signature did not verify */
|
|
63
|
+
class InvalidSignatureError extends AlgoPayError {
|
|
64
|
+
constructor() {
|
|
65
|
+
super("Signature verification failed", "INVALID_SIGNATURE");
|
|
66
|
+
this.name = "InvalidSignatureError";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/** Configuration is missing or invalid */
|
|
71
|
+
class ConfigError extends AlgoPayError {
|
|
72
|
+
constructor(message) {
|
|
73
|
+
super(message, "CONFIG_ERROR");
|
|
74
|
+
this.name = "ConfigError";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Payment provider (Razorpay/Stripe) signature/HMAC check failed */
|
|
79
|
+
class ProviderAuthError extends AlgoPayError {
|
|
80
|
+
constructor(provider, reason) {
|
|
81
|
+
super(`${provider} signature verification failed: ${reason}`, "PROVIDER_AUTH_ERROR");
|
|
82
|
+
this.name = "ProviderAuthError";
|
|
83
|
+
this.provider = provider;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
module.exports = {
|
|
88
|
+
AlgoPayError,
|
|
89
|
+
InsufficientAmountError,
|
|
90
|
+
ProofExpiredError,
|
|
91
|
+
OracleNotRegisteredError,
|
|
92
|
+
ReplayError,
|
|
93
|
+
InvalidSignatureError,
|
|
94
|
+
ConfigError,
|
|
95
|
+
ProviderAuthError,
|
|
96
|
+
};
|