@usdctofiat/offramp 1.3.0 → 2.0.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/dist/index.cjs CHANGED
@@ -22,12 +22,15 @@ var index_exports = {};
22
22
  __export(index_exports, {
23
23
  CURRENCIES: () => CURRENCIES,
24
24
  ESCROW_ADDRESS: () => ESCROW_ADDRESS,
25
+ MakersCreateError: () => MakersCreateError,
25
26
  OFFRAMP_ERROR_CODES: () => OFFRAMP_ERROR_CODES,
26
27
  Offramp: () => Offramp,
27
28
  OfframpError: () => OfframpError,
29
+ PEER_EXTENSION_CHROME_URL: () => import_sdk7.PEER_EXTENSION_CHROME_URL,
28
30
  PLATFORMS: () => PLATFORMS,
29
31
  close: () => close,
30
32
  createOfframp: () => createOfframp,
33
+ createPeerExtensionSdk: () => import_sdk7.createPeerExtensionSdk,
31
34
  createTelemetryContext: () => createTelemetryContext,
32
35
  delegate: () => delegate,
33
36
  deposits: () => deposits,
@@ -35,7 +38,14 @@ __export(index_exports, {
35
38
  emitEvent: () => emitEvent,
36
39
  enableOtc: () => enableOtc,
37
40
  getOtcLink: () => getOtcLink,
41
+ getPeerExtensionRegistrationInfo: () => getPeerExtensionRegistrationInfo,
42
+ getPeerExtensionState: () => import_sdk7.getPeerExtensionState,
43
+ isPeerExtensionAvailable: () => import_sdk7.isPeerExtensionAvailable,
44
+ isPeerExtensionRegistrationError: () => isPeerExtensionRegistrationError,
45
+ normalizePaypalMeUsername: () => normalizePaypalMeUsername,
38
46
  offramp: () => offramp,
47
+ openPeerExtensionInstallPage: () => import_sdk7.openPeerExtensionInstallPage,
48
+ peerExtensionSdk: () => import_sdk7.peerExtensionSdk,
39
49
  sanitizeAttributionId: () => sanitizeAttributionId,
40
50
  sendTelemetryEvent: () => sendTelemetryEvent,
41
51
  undelegate: () => undelegate
@@ -166,6 +176,17 @@ function resolveSupportedCurrencies(platform) {
166
176
  }
167
177
  return Array.from(codes).sort();
168
178
  }
179
+ function normalizePaypalMeUsername(value) {
180
+ const trimmed = value.trim();
181
+ if (!trimmed) return "";
182
+ const withoutProtocol = trimmed.replace(/^https?:\/\//i, "").replace(/^www\./i, "");
183
+ if (/^paypal\.me(?:[?#].*)?$/i.test(withoutProtocol)) return "";
184
+ const withoutDomain = withoutProtocol.replace(/^paypal\.me\//i, "");
185
+ const [pathWithoutQuery] = withoutDomain.split(/[?#]/, 1);
186
+ const sanitizedPath = pathWithoutQuery.replace(/^\/+/, "");
187
+ const [username] = sanitizedPath.split("/", 1);
188
+ return username.replace(/^@+/, "").trim().toLowerCase();
189
+ }
169
190
  var BLUEPRINTS = {
170
191
  venmo: {
171
192
  id: "venmo",
@@ -213,7 +234,13 @@ var BLUEPRINTS = {
213
234
  placeholder: "wisetag (no @)",
214
235
  helperText: "Your Wise @wisetag (no @)",
215
236
  validation: import_zod.z.string().min(1).regex(/^[a-zA-Z0-9_-]+$/),
216
- transform: (v) => v.replace(/^@+/, "").trim()
237
+ transform: (v) => v.replace(/^@+/, "").trim(),
238
+ extensionRegistration: {
239
+ providerId: "wise",
240
+ requiredPrompt: "This Wisetag is not registered yet. Verify it in the Peer extension, then refresh and retry with the same Wisetag.",
241
+ ctaLabel: "Install Peer Extension",
242
+ minExtensionVersion: "0.4.12"
243
+ }
217
244
  },
218
245
  mercadopago: {
219
246
  id: "mercadopago",
@@ -234,10 +261,18 @@ var BLUEPRINTS = {
234
261
  paypal: {
235
262
  id: "paypal",
236
263
  name: "PayPal",
237
- identifierLabel: "Email",
238
- placeholder: "email",
239
- helperText: "Email linked to PayPal account",
240
- validation: import_zod.z.string().email()
264
+ identifierLabel: "PayPal.me Username",
265
+ placeholder: "paypal.me/your-username",
266
+ helperText: "Your PayPal.me username, not your email",
267
+ validation: import_zod.z.string().min(1, "PayPal.me username is required").regex(/^[a-z0-9._-]+$/i, "Use your PayPal.me username (letters, numbers, . _ -)"),
268
+ transform: normalizePaypalMeUsername,
269
+ extensionRegistration: {
270
+ providerId: "paypal",
271
+ requiredPrompt: "This PayPal username is not registered yet. Verify it in the Peer extension, then refresh and retry with the same PayPal username.",
272
+ ctaLabel: "Install Peer Extension",
273
+ ctaSubtext: "PayPal Business accounts are not supported at this time.",
274
+ minExtensionVersion: "0.4.14"
275
+ }
241
276
  },
242
277
  monzo: {
243
278
  id: "monzo",
@@ -268,6 +303,7 @@ function buildPlatformEntry(bp) {
268
303
  placeholder: bp.placeholder,
269
304
  help: bp.helperText
270
305
  },
306
+ ...bp.extensionRegistration ? { extensionRegistration: bp.extensionRegistration } : {},
271
307
  validate(input) {
272
308
  const transformed = bp.transform ? bp.transform(input) : input;
273
309
  const result = bp.validation.safeParse(transformed);
@@ -294,6 +330,9 @@ var PLATFORMS = {
294
330
  MONZO: buildPlatformEntry(BLUEPRINTS.monzo),
295
331
  N26: buildPlatformEntry(BLUEPRINTS.n26)
296
332
  };
333
+ function getPlatformById(id) {
334
+ return Object.values(PLATFORMS).find((p) => p.id === id) ?? null;
335
+ }
297
336
  function normalizePaymentMethodLookupName(platform) {
298
337
  const normalized = platform.trim().toLowerCase();
299
338
  if (!normalized) return null;
@@ -337,34 +376,36 @@ function getPaymentMethodHashes(platform) {
337
376
  }
338
377
  return Array.from(hashes);
339
378
  }
340
- function buildDepositData(platform, identifier) {
341
- switch (platform) {
342
- case "venmo":
343
- return { venmoUsername: identifier, telegramUsername: "" };
344
- case "cashapp":
345
- return { cashtag: identifier, telegramUsername: "" };
346
- case "chime":
347
- return { chimesign: identifier.toLowerCase(), telegramUsername: "" };
348
- case "revolut":
349
- return { revolutUsername: identifier, telegramUsername: "" };
350
- case "wise":
351
- return { wisetag: identifier, telegramUsername: "" };
352
- case "mercadopago":
353
- return { cvu: identifier, telegramUsername: "" };
354
- case "zelle":
355
- return { zelleEmail: identifier, telegramUsername: "" };
356
- case "paypal":
357
- return { paypalEmail: identifier, telegramUsername: "" };
358
- case "monzo":
359
- return { monzoMeUsername: identifier, telegramUsername: "" };
360
- case "n26":
361
- return { iban: identifier, telegramUsername: "" };
362
- default:
363
- return { identifier, telegramUsername: "" };
364
- }
379
+ function buildDepositData(platform, identifier, telegramUsername = "") {
380
+ const offchainId = platform === "chime" ? identifier.toLowerCase() : identifier;
381
+ return telegramUsername ? { offchainId, telegramUsername } : { offchainId };
382
+ }
383
+ function getPeerExtensionRegistrationInfo(platform) {
384
+ const entry = getPlatformById(platform);
385
+ return entry?.extensionRegistration ?? null;
386
+ }
387
+ var EXTENSION_REGISTRATION_ERROR_PATTERNS = ["creating the maker", "create the maker"];
388
+ function isPeerExtensionRegistrationError(platform, message, statusCode) {
389
+ const info = getPeerExtensionRegistrationInfo(platform);
390
+ if (!info) return false;
391
+ if (statusCode === 400) return true;
392
+ const normalized = (message ?? "").trim();
393
+ if (!normalized) return false;
394
+ const lower = normalized.toLowerCase();
395
+ return EXTENSION_REGISTRATION_ERROR_PATTERNS.some((p) => lower.includes(p));
365
396
  }
366
397
 
367
398
  // src/errors.ts
399
+ var MakersCreateError = class extends Error {
400
+ status;
401
+ body;
402
+ constructor(message, status, body) {
403
+ super(message);
404
+ this.name = "MakersCreateError";
405
+ this.status = status;
406
+ this.body = body;
407
+ }
408
+ };
368
409
  var OfframpError = class extends Error {
369
410
  code;
370
411
  step;
@@ -1153,23 +1194,39 @@ function extractDepositIdFromLogs(logs) {
1153
1194
  }
1154
1195
  return null;
1155
1196
  }
1156
- async function registerPayeeDetails(processorName, depositData) {
1197
+ async function registerPayeeDetails(processorName, payload) {
1157
1198
  const controller = new AbortController();
1158
1199
  const timeout = setTimeout(() => controller.abort(), PAYEE_REGISTRATION_TIMEOUT_MS);
1159
1200
  try {
1160
- const res = await fetch(`${API_BASE_URL}/v1/makers/create`, {
1201
+ const res = await fetch(`${API_BASE_URL}/v2/makers/create`, {
1161
1202
  method: "POST",
1162
1203
  headers: { "Content-Type": "application/json" },
1163
- body: JSON.stringify({ processorName, depositData }),
1204
+ body: JSON.stringify({ processorName, ...payload }),
1164
1205
  signal: controller.signal
1165
1206
  });
1166
1207
  if (!res.ok) {
1167
1208
  const txt = await res.text().catch(() => "");
1168
- throw new Error(`makers/create failed (${res.status}): ${txt || res.statusText}`);
1209
+ let detail = txt || res.statusText;
1210
+ try {
1211
+ const parsed = JSON.parse(txt);
1212
+ if (typeof parsed.message === "string" && parsed.message.trim()) {
1213
+ detail = parsed.message;
1214
+ }
1215
+ } catch {
1216
+ }
1217
+ throw new MakersCreateError(
1218
+ `makers/create failed (${res.status}): ${detail}`,
1219
+ res.status,
1220
+ txt
1221
+ );
1169
1222
  }
1170
1223
  const json = await res.json();
1171
1224
  if (!json.success || !json.responseObject?.hashedOnchainId) {
1172
- throw new Error(json.message || "makers/create returned no hashedOnchainId");
1225
+ throw new MakersCreateError(
1226
+ json.message || "makers/create returned no hashedOnchainId",
1227
+ json.statusCode ?? null,
1228
+ ""
1229
+ );
1173
1230
  }
1174
1231
  return json.responseObject.hashedOnchainId;
1175
1232
  } finally {
@@ -1371,11 +1428,21 @@ async function offramp(walletClient, params, onProgress, telemetryContext) {
1371
1428
  }
1372
1429
  emitProgress({ step: "registering" });
1373
1430
  let hashedOnchainId;
1431
+ const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
1374
1432
  try {
1375
- const canonicalName = platformId.startsWith("zelle") ? "zelle" : platformId;
1376
- const depositData = buildDepositData(platformId, normalizedIdentifier);
1377
- hashedOnchainId = await registerPayeeDetails(canonicalName, depositData);
1433
+ const payeePayload = buildDepositData(canonicalName, normalizedIdentifier);
1434
+ hashedOnchainId = await registerPayeeDetails(canonicalName, payeePayload);
1378
1435
  } catch (err) {
1436
+ const status = err instanceof MakersCreateError ? err.status : null;
1437
+ const message = err instanceof Error ? err.message : String(err);
1438
+ if (isPeerExtensionRegistrationError(canonicalName, message, status)) {
1439
+ throw new OfframpError(
1440
+ `${platform.name} maker is not yet registered in the Peer extension. Verify this handle in the extension, then retry.`,
1441
+ "EXTENSION_REGISTRATION_REQUIRED",
1442
+ "registering",
1443
+ err
1444
+ );
1445
+ }
1379
1446
  throw new OfframpError(
1380
1447
  "Payee registration failed",
1381
1448
  "REGISTRATION_FAILED",
@@ -1713,22 +1780,37 @@ var OFFRAMP_ERROR_CODES = {
1713
1780
  VALIDATION: "VALIDATION",
1714
1781
  APPROVAL_FAILED: "APPROVAL_FAILED",
1715
1782
  REGISTRATION_FAILED: "REGISTRATION_FAILED",
1783
+ /**
1784
+ * Curator rejected the maker because the user has not registered this
1785
+ * handle in the Peer extension yet. Thrown from `offramp()` for PayPal
1786
+ * and Wise when `/v2/makers/create` returns 400 or a "creating the maker"
1787
+ * message. Recover by prompting the user through the Peer extension via
1788
+ * `usePeerExtensionRegistration(platform)` (React) or `peerExtensionSdk`
1789
+ * directly, then call `offramp()` again.
1790
+ */
1791
+ EXTENSION_REGISTRATION_REQUIRED: "EXTENSION_REGISTRATION_REQUIRED",
1716
1792
  DEPOSIT_FAILED: "DEPOSIT_FAILED",
1717
1793
  CONFIRMATION_FAILED: "CONFIRMATION_FAILED",
1718
1794
  DELEGATION_FAILED: "DELEGATION_FAILED",
1719
1795
  USER_CANCELLED: "USER_CANCELLED",
1720
1796
  UNSUPPORTED: "UNSUPPORTED"
1721
1797
  };
1798
+
1799
+ // src/extension.ts
1800
+ var import_sdk7 = require("@zkp2p/sdk");
1722
1801
  // Annotate the CommonJS export names for ESM import in node:
1723
1802
  0 && (module.exports = {
1724
1803
  CURRENCIES,
1725
1804
  ESCROW_ADDRESS,
1805
+ MakersCreateError,
1726
1806
  OFFRAMP_ERROR_CODES,
1727
1807
  Offramp,
1728
1808
  OfframpError,
1809
+ PEER_EXTENSION_CHROME_URL,
1729
1810
  PLATFORMS,
1730
1811
  close,
1731
1812
  createOfframp,
1813
+ createPeerExtensionSdk,
1732
1814
  createTelemetryContext,
1733
1815
  delegate,
1734
1816
  deposits,
@@ -1736,7 +1818,14 @@ var OFFRAMP_ERROR_CODES = {
1736
1818
  emitEvent,
1737
1819
  enableOtc,
1738
1820
  getOtcLink,
1821
+ getPeerExtensionRegistrationInfo,
1822
+ getPeerExtensionState,
1823
+ isPeerExtensionAvailable,
1824
+ isPeerExtensionRegistrationError,
1825
+ normalizePaypalMeUsername,
1739
1826
  offramp,
1827
+ openPeerExtensionInstallPage,
1828
+ peerExtensionSdk,
1740
1829
  sanitizeAttributionId,
1741
1830
  sendTelemetryEvent,
1742
1831
  undelegate