bison-web-components 1.2.0 → 1.5.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/bison-operator-payments.js +701 -25
- package/package.json +1 -1
|
@@ -90,6 +90,639 @@ const BOP_ICONS = {
|
|
|
90
90
|
plus: '<svg viewBox="0 0 24 24" width="14" height="14" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M12 5v14"/><path d="M5 12h14"/></svg>',
|
|
91
91
|
};
|
|
92
92
|
|
|
93
|
+
class BisonJibPayAPI {
|
|
94
|
+
constructor(baseURL, embeddableKey) {
|
|
95
|
+
if (!embeddableKey || typeof embeddableKey !== 'string' || !embeddableKey.trim()) {
|
|
96
|
+
throw new Error("Missing required 'x-embeddable-key' for BisonJibPayAPI");
|
|
97
|
+
}
|
|
98
|
+
this.baseURL = baseURL || "https://bison-jib-development.azurewebsites.net";
|
|
99
|
+
this.embeddableKey = embeddableKey;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Make authenticated API request
|
|
104
|
+
* @private
|
|
105
|
+
*/
|
|
106
|
+
async request(endpoint, options = {}) {
|
|
107
|
+
if (!this.embeddableKey || !this.embeddableKey.trim()) {
|
|
108
|
+
throw new Error("Missing required 'x-embeddable-key' for BisonJibPayAPI request");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const url = `${this.baseURL}${endpoint}`;
|
|
112
|
+
const headers = {
|
|
113
|
+
"X-Embeddable-Key": this.embeddableKey,
|
|
114
|
+
...options.headers,
|
|
115
|
+
};
|
|
116
|
+
|
|
117
|
+
// Don't add Content-Type for FormData
|
|
118
|
+
if (!(options.body instanceof FormData)) {
|
|
119
|
+
headers["Content-Type"] = "application/json";
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
try {
|
|
123
|
+
const response = await fetch(url, {
|
|
124
|
+
...options,
|
|
125
|
+
headers,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const data = await response.json();
|
|
129
|
+
|
|
130
|
+
if (!response.ok) {
|
|
131
|
+
throw {
|
|
132
|
+
status: response.status,
|
|
133
|
+
data: data,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
return data;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
// Re-throw with structured error
|
|
140
|
+
if (error.status) throw error;
|
|
141
|
+
throw {
|
|
142
|
+
status: 500,
|
|
143
|
+
data: {
|
|
144
|
+
success: false,
|
|
145
|
+
message: "Network error occurred",
|
|
146
|
+
errors: [error.message],
|
|
147
|
+
},
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Validate operator email
|
|
154
|
+
*
|
|
155
|
+
* @param {string} email - Operator's email address
|
|
156
|
+
* @param {string} operatorId - Operator's ID
|
|
157
|
+
* @param {string|null} clientId - Optional client ID
|
|
158
|
+
*/
|
|
159
|
+
async validateOperatorEmail(email, operatorId, clientId = null) {
|
|
160
|
+
const payload = { email, operatorId };
|
|
161
|
+
if (clientId) payload.clientId = clientId;
|
|
162
|
+
return this.request("/api/embeddable/validate/operator-email", {
|
|
163
|
+
method: "POST",
|
|
164
|
+
body: JSON.stringify(payload),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Validate user email
|
|
170
|
+
*
|
|
171
|
+
* Checks if a user email exists in the system.
|
|
172
|
+
*
|
|
173
|
+
* @param {string} email - User's email address
|
|
174
|
+
* @returns {Promise<{success: boolean, message: string, data: {exists: boolean, message: string}, errors: string[], timestamp: string, traceId: string}>}
|
|
175
|
+
*
|
|
176
|
+
* @example
|
|
177
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
178
|
+
* const result = await api.validateUserEmail('user@example.com');
|
|
179
|
+
* if (result.data.exists) {
|
|
180
|
+
* console.log('User email exists');
|
|
181
|
+
* }
|
|
182
|
+
*/
|
|
183
|
+
async validateUserEmail(email) {
|
|
184
|
+
return this.request("/api/embeddable/validate/user-email", {
|
|
185
|
+
method: "POST",
|
|
186
|
+
body: JSON.stringify({ email }),
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Verify operator email
|
|
192
|
+
* Checks if an operator is registered/onboarded in the system
|
|
193
|
+
*
|
|
194
|
+
* @param {string} email - Operator's email address
|
|
195
|
+
* @param {string} operatorId - Operator's ID
|
|
196
|
+
* @param {string|null} clientId - Optional client ID
|
|
197
|
+
* @returns {Promise<{success: boolean, message: string, data?: any}>}
|
|
198
|
+
*
|
|
199
|
+
* @example
|
|
200
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
201
|
+
* const result = await api.verifyOperator('operator@example.com', 'OP123456');
|
|
202
|
+
* if (result.success) {
|
|
203
|
+
* console.log('Operator is verified');
|
|
204
|
+
* }
|
|
205
|
+
*/
|
|
206
|
+
async verifyOperator(email, operatorId, clientId = null) {
|
|
207
|
+
return this.validateOperatorEmail(email, operatorId, clientId);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Verify WIO email
|
|
212
|
+
* Checks if a WIO (Worker Independent Operator) has an account in the system
|
|
213
|
+
*
|
|
214
|
+
* @param {string} email - WIO's email address
|
|
215
|
+
* @returns {Promise<{success: boolean, message: string, data?: {moovAccountId: string}}>}
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
219
|
+
* const result = await api.verifyWio('wio@example.com');
|
|
220
|
+
* if (result.success && result.data?.moovAccountId) {
|
|
221
|
+
* console.log('WIO is verified with account:', result.data.moovAccountId);
|
|
222
|
+
* }
|
|
223
|
+
*/
|
|
224
|
+
async verifyWio(email) {
|
|
225
|
+
return this.getAccountByEmail(email);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Register operator
|
|
230
|
+
*/
|
|
231
|
+
async registerOperator(formData) {
|
|
232
|
+
return this.request("/api/embeddable/operator-registration", {
|
|
233
|
+
method: "POST",
|
|
234
|
+
body: formData, // FormData object
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Register WIO
|
|
240
|
+
*/
|
|
241
|
+
async registerWIO(payload) {
|
|
242
|
+
return this.request("/api/embeddable/wio-registration", {
|
|
243
|
+
method: "POST",
|
|
244
|
+
body: payload, // FormData object
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
async getAccountByEmail(operatorEmail) {
|
|
249
|
+
const param = new URLSearchParams();
|
|
250
|
+
param.append("email", operatorEmail);
|
|
251
|
+
|
|
252
|
+
return this.request(`/api/embeddable/moov-account-id?${param.toString()}`, {
|
|
253
|
+
method: "GET",
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
async getAccountByOperatorId(operatorId) {
|
|
258
|
+
const param = new URLSearchParams();
|
|
259
|
+
param.append("operatorId", operatorId);
|
|
260
|
+
|
|
261
|
+
return this.request(`/api/embeddable/moov-account-id?${param.toString()}`, {
|
|
262
|
+
method: "GET",
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async getAccountByClientId(clientId) {
|
|
267
|
+
const param = new URLSearchParams();
|
|
268
|
+
param.append("clientId", clientId);
|
|
269
|
+
|
|
270
|
+
return this.request(`/api/embeddable/moov-account-id?${param.toString()}`, {
|
|
271
|
+
method: "GET",
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Generate Moov access token for operator
|
|
277
|
+
*
|
|
278
|
+
* This method calls the backend API to generate a Moov token for payment operations.
|
|
279
|
+
* The backend handles the secure communication with Moov's API.
|
|
280
|
+
*
|
|
281
|
+
* @param {string} operatorEmail - Operator's email address
|
|
282
|
+
* @param {string|null} moovAccountId - Optional Moov account ID
|
|
283
|
+
* @param {string|null} operatorId - Optional operator ID
|
|
284
|
+
* @param {string|null} clientId - Optional client ID
|
|
285
|
+
* @returns {Promise<{access_token: string, expires_in?: number, scope?: string}>}
|
|
286
|
+
*
|
|
287
|
+
* @example
|
|
288
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
289
|
+
* const tokenData = await api.generateMoovToken('operator@example.com');
|
|
290
|
+
* console.log(tokenData.access_token);
|
|
291
|
+
*/
|
|
292
|
+
async generateMoovToken(operatorEmail, moovAccountId = null, operatorId = null, clientId = null) {
|
|
293
|
+
console.log("CALLED GENERATE MOOV TOKEN");
|
|
294
|
+
|
|
295
|
+
// Use provided moovAccountId or fetch it if not provided
|
|
296
|
+
let accountId = moovAccountId;
|
|
297
|
+
if (!accountId) {
|
|
298
|
+
if (operatorEmail) {
|
|
299
|
+
const account = await this.getAccountByEmail(operatorEmail);
|
|
300
|
+
accountId = account.data.moovAccountId;
|
|
301
|
+
} else if (operatorId) {
|
|
302
|
+
const account = await this.getAccountByOperatorId(operatorId);
|
|
303
|
+
accountId = account.data.moovAccountId;
|
|
304
|
+
} else if (clientId) {
|
|
305
|
+
const account = await this.getAccountByClientId(clientId);
|
|
306
|
+
accountId = account.data.moovAccountId;
|
|
307
|
+
} else {
|
|
308
|
+
throw {
|
|
309
|
+
status: 400,
|
|
310
|
+
data: {
|
|
311
|
+
success: false,
|
|
312
|
+
message: "Email, operator ID, or client ID is required to generate a token",
|
|
313
|
+
errors: ["Missing operator identifier"],
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
console.log("MOOV ACCOUNT ID", accountId);
|
|
319
|
+
let accountScopes = [
|
|
320
|
+
"/accounts/{ACCOUNT_ID}/bank-accounts.read",
|
|
321
|
+
"/accounts/{ACCOUNT_ID}/bank-accounts.write",
|
|
322
|
+
"/accounts/{ACCOUNT_ID}/capabilities.read",
|
|
323
|
+
"/accounts/{ACCOUNT_ID}/capabilities.write",
|
|
324
|
+
"/accounts/{ACCOUNT_ID}/cards.read",
|
|
325
|
+
"/accounts/{ACCOUNT_ID}/cards.write",
|
|
326
|
+
"/accounts/{ACCOUNT_ID}/profile.read",
|
|
327
|
+
"/accounts/{ACCOUNT_ID}/profile.write",
|
|
328
|
+
"/accounts/{ACCOUNT_ID}/representatives.read",
|
|
329
|
+
"/accounts/{ACCOUNT_ID}/representatives.write",
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
if (accountId) {
|
|
333
|
+
accountScopes = accountScopes.map((value) =>
|
|
334
|
+
value.replace("{ACCOUNT_ID}", accountId)
|
|
335
|
+
);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const tokenPayload = {
|
|
339
|
+
scopes: [
|
|
340
|
+
"/accounts.read",
|
|
341
|
+
"/accounts.write",
|
|
342
|
+
"/fed.read",
|
|
343
|
+
"/profile-enrichment.read",
|
|
344
|
+
...accountScopes,
|
|
345
|
+
],
|
|
346
|
+
};
|
|
347
|
+
|
|
348
|
+
if (operatorEmail) {
|
|
349
|
+
tokenPayload.email = operatorEmail;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
if (operatorId) {
|
|
353
|
+
tokenPayload.operatorId = operatorId;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (clientId) {
|
|
357
|
+
tokenPayload.clientId = clientId;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return this.request("/api/embeddable/moov-access-token", {
|
|
361
|
+
method: "POST",
|
|
362
|
+
body: JSON.stringify(tokenPayload),
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Generate Plaid Link token for WIO
|
|
368
|
+
*
|
|
369
|
+
* This method calls the backend API to generate a Plaid Link token for bank account linking.
|
|
370
|
+
* The token is used to initialize Plaid Link in the Moov payment drop.
|
|
371
|
+
*
|
|
372
|
+
* @param {string} wioEmail - WIO's email address
|
|
373
|
+
* @returns {Promise<{link_token: string, expiration?: string}>}
|
|
374
|
+
*
|
|
375
|
+
* @example
|
|
376
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
377
|
+
* const tokenData = await api.generatePlaidToken('wio@example.com');
|
|
378
|
+
* console.log(tokenData.link_token);
|
|
379
|
+
*/
|
|
380
|
+
async generatePlaidToken(wioEmail) {
|
|
381
|
+
return this.request("/api/embeddable/plaid/link-token", {
|
|
382
|
+
method: "POST",
|
|
383
|
+
body: JSON.stringify({
|
|
384
|
+
clientName: wioEmail,
|
|
385
|
+
countryCodes: ["US"],
|
|
386
|
+
user: {
|
|
387
|
+
clientUserId: "wio-email",
|
|
388
|
+
legalName: "Wio User",
|
|
389
|
+
},
|
|
390
|
+
products: ["transactions"],
|
|
391
|
+
client_name: "Personal Finance App",
|
|
392
|
+
}),
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
/**
|
|
397
|
+
* Create Plaid processor token
|
|
398
|
+
*
|
|
399
|
+
* Exchanges a Plaid public token for a processor token that can be used with Moov.
|
|
400
|
+
* This is called during the Plaid Link flow after the user selects their bank account.
|
|
401
|
+
*
|
|
402
|
+
* @param {string} publicToken - Plaid public token from Link flow
|
|
403
|
+
* @param {string} bankAccountId - Selected bank account ID
|
|
404
|
+
* @returns {Promise<{processor_token: string, bank_account_id: string}>}
|
|
405
|
+
*
|
|
406
|
+
* @example
|
|
407
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
408
|
+
* const result = await api.createProcessorToken(publicToken, accountId);
|
|
409
|
+
* console.log(result.processor_token);
|
|
410
|
+
*/
|
|
411
|
+
async createProcessorToken(publicToken, bankAccountId) {
|
|
412
|
+
return this.request("/api/embeddable/plaid/processor-token", {
|
|
413
|
+
method: "POST",
|
|
414
|
+
body: JSON.stringify({
|
|
415
|
+
publicToken,
|
|
416
|
+
accountId: bankAccountId,
|
|
417
|
+
}),
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
async addPlaidAccountToMoov(publicToken, bankAccountId, moovAccountId) {
|
|
422
|
+
return this.request("/api/embeddable/plaid/add-to-moov", {
|
|
423
|
+
method: "POST",
|
|
424
|
+
body: JSON.stringify({
|
|
425
|
+
publicToken,
|
|
426
|
+
moovAccountId,
|
|
427
|
+
accountId: bankAccountId,
|
|
428
|
+
}),
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
/**
|
|
433
|
+
* Get payment methods by moovAccountId directly
|
|
434
|
+
*
|
|
435
|
+
* This method fetches all available payment methods for the given moovAccountId.
|
|
436
|
+
* Use this when you already have the moovAccountId cached to avoid extra API calls.
|
|
437
|
+
*
|
|
438
|
+
* @param {string} moovAccountId - The Moov account ID
|
|
439
|
+
* @returns {Promise<{success: boolean, message: string, data: Array<{paymentMethodID: string, paymentMethodType: string, wallet?: object, bankAccount?: object, card?: object, applePay?: object}>, errors: string[], timestamp: string, traceId: string}>}
|
|
440
|
+
*
|
|
441
|
+
* @example
|
|
442
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
443
|
+
* const paymentMethods = await api.getPaymentMethodsByAccountId('moov-account-id');
|
|
444
|
+
* console.log(paymentMethods.data); // Array of payment methods
|
|
445
|
+
*/
|
|
446
|
+
async getPaymentMethodsByAccountId(moovAccountId) {
|
|
447
|
+
if (!moovAccountId) {
|
|
448
|
+
throw {
|
|
449
|
+
status: 400,
|
|
450
|
+
data: {
|
|
451
|
+
success: false,
|
|
452
|
+
message: "Moov account ID is required",
|
|
453
|
+
errors: ["moovAccountId parameter is missing"],
|
|
454
|
+
},
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
return this.request(`/api/embeddable/payment-methods/${moovAccountId}`, {
|
|
459
|
+
method: "GET",
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Get payment methods for an operator by email
|
|
465
|
+
*
|
|
466
|
+
* This method first retrieves the operator's moovAccountId by email,
|
|
467
|
+
* then fetches all available payment methods for that account.
|
|
468
|
+
* Note: If you already have the moovAccountId, use getPaymentMethodsByAccountId() instead
|
|
469
|
+
* to avoid the extra API call.
|
|
470
|
+
*
|
|
471
|
+
* @param {string} operatorEmail - Operator's email address
|
|
472
|
+
* @returns {Promise<{success: boolean, message: string, data: Array<{paymentMethodID: string, paymentMethodType: string, wallet?: object, bankAccount?: object, card?: object, applePay?: object}>, errors: string[], timestamp: string, traceId: string}>}
|
|
473
|
+
*
|
|
474
|
+
* @example
|
|
475
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
476
|
+
* const paymentMethods = await api.getPaymentMethods('operator@example.com');
|
|
477
|
+
* console.log(paymentMethods.data); // Array of payment methods
|
|
478
|
+
*/
|
|
479
|
+
async getPaymentMethods(operatorEmail) {
|
|
480
|
+
// First, get the account by email to retrieve moovAccountId
|
|
481
|
+
const account = await this.getAccountByEmail(operatorEmail);
|
|
482
|
+
const moovAccountId = account.data?.moovAccountId || account.moovAccountId;
|
|
483
|
+
|
|
484
|
+
if (!moovAccountId) {
|
|
485
|
+
throw {
|
|
486
|
+
status: 404,
|
|
487
|
+
data: {
|
|
488
|
+
success: false,
|
|
489
|
+
message: "Moov account ID not found for the given email",
|
|
490
|
+
errors: ["No moovAccountId associated with this operator"],
|
|
491
|
+
},
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
// Use the direct method to fetch payment methods
|
|
496
|
+
return this.getPaymentMethodsByAccountId(moovAccountId);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/**
|
|
500
|
+
* Delete a payment method by moovAccountId and paymentMethodId directly
|
|
501
|
+
*
|
|
502
|
+
* Use this when you already have the moovAccountId cached to avoid extra API calls.
|
|
503
|
+
*
|
|
504
|
+
* @param {string} moovAccountId - The Moov account ID
|
|
505
|
+
* @param {string} paymentMethodId - The ID of the payment method to delete
|
|
506
|
+
* @returns {Promise<{success: boolean, message: string, data: string, errors: string[], timestamp: string, traceId: string}>}
|
|
507
|
+
*
|
|
508
|
+
* @example
|
|
509
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
510
|
+
* const result = await api.deletePaymentMethodByAccountId('moov-account-id', 'pm_123456');
|
|
511
|
+
* console.log(result.success); // true if deleted successfully
|
|
512
|
+
*/
|
|
513
|
+
async deletePaymentMethodByAccountId(moovAccountId, paymentMethodId) {
|
|
514
|
+
if (!moovAccountId) {
|
|
515
|
+
throw {
|
|
516
|
+
status: 400,
|
|
517
|
+
data: {
|
|
518
|
+
success: false,
|
|
519
|
+
message: "Moov account ID is required",
|
|
520
|
+
errors: ["moovAccountId parameter is missing"],
|
|
521
|
+
},
|
|
522
|
+
};
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
if (!paymentMethodId) {
|
|
526
|
+
throw {
|
|
527
|
+
status: 400,
|
|
528
|
+
data: {
|
|
529
|
+
success: false,
|
|
530
|
+
message: "Payment method ID is required",
|
|
531
|
+
errors: ["paymentMethodId parameter is missing"],
|
|
532
|
+
},
|
|
533
|
+
};
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return this.request(
|
|
537
|
+
`/api/embeddable/bank-account/${moovAccountId}/${paymentMethodId}`,
|
|
538
|
+
{
|
|
539
|
+
method: "DELETE",
|
|
540
|
+
}
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
/**
|
|
545
|
+
* Delete a payment method by ID
|
|
546
|
+
*
|
|
547
|
+
* This method first retrieves the operator's moovAccountId by email,
|
|
548
|
+
* then deletes the specified payment method.
|
|
549
|
+
* Note: If you already have the moovAccountId, use deletePaymentMethodByAccountId() instead
|
|
550
|
+
* to avoid the extra API call.
|
|
551
|
+
*
|
|
552
|
+
* @param {string} operatorEmail - Operator's email address
|
|
553
|
+
* @param {string} paymentMethodId - The ID of the payment method to delete
|
|
554
|
+
* @returns {Promise<{success: boolean, message: string, data: string, errors: string[], timestamp: string, traceId: string}>}
|
|
555
|
+
*
|
|
556
|
+
* @example
|
|
557
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
558
|
+
* const result = await api.deletePaymentMethodById('operator@example.com', 'pm_123456');
|
|
559
|
+
* console.log(result.success); // true if deleted successfully
|
|
560
|
+
*/
|
|
561
|
+
async deletePaymentMethodById(operatorEmail, paymentMethodId) {
|
|
562
|
+
// First, get the account by email to retrieve moovAccountId
|
|
563
|
+
const account = await this.getAccountByEmail(operatorEmail);
|
|
564
|
+
const moovAccountId = account.data?.moovAccountId || account.moovAccountId;
|
|
565
|
+
|
|
566
|
+
if (!moovAccountId) {
|
|
567
|
+
throw {
|
|
568
|
+
status: 404,
|
|
569
|
+
data: {
|
|
570
|
+
success: false,
|
|
571
|
+
message: "Moov account ID not found for the given email",
|
|
572
|
+
errors: ["No moovAccountId associated with this operator"],
|
|
573
|
+
},
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
// Use the direct method to delete payment method
|
|
578
|
+
return this.deletePaymentMethodByAccountId(moovAccountId, paymentMethodId);
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Fetch underwriting history by moovAccountId
|
|
582
|
+
*
|
|
583
|
+
* This method retrieves the underwriting history for the given moovAccountId.
|
|
584
|
+
* Use this when you already have the moovAccountId cached.
|
|
585
|
+
*
|
|
586
|
+
* Response Codes:
|
|
587
|
+
* - 200: Success with data array (may be empty)
|
|
588
|
+
* - 400: Missing or invalid moovAccountId parameter
|
|
589
|
+
* - 401: Invalid or missing X-Embeddable-Key header
|
|
590
|
+
* - 404: Moov account with specified ID not found
|
|
591
|
+
* - 500: Server error while retrieving underwriting history
|
|
592
|
+
*
|
|
593
|
+
* @param {string} moovAccountId - The Moov account ID
|
|
594
|
+
* @returns {Promise<{success: boolean, message?: string, data: Array|null, errors: string[], timestamp?: string, traceId?: string}>}
|
|
595
|
+
*
|
|
596
|
+
* @example
|
|
597
|
+
* const api = new BisonJibPayAPI(baseURL, embeddableKey);
|
|
598
|
+
* const history = await api.fetchUnderwritingByAccountId('moov-account-id');
|
|
599
|
+
* console.log(history.data); // Array of underwriting history records
|
|
600
|
+
*/
|
|
601
|
+
async fetchUnderwritingByAccountId(moovAccountId) {
|
|
602
|
+
if (!moovAccountId) {
|
|
603
|
+
throw {
|
|
604
|
+
status: 400,
|
|
605
|
+
data: {
|
|
606
|
+
success: false,
|
|
607
|
+
message: "Moov account ID is required",
|
|
608
|
+
errors: ["moovAccountId parameter is missing"],
|
|
609
|
+
},
|
|
610
|
+
};
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
return this.request(
|
|
614
|
+
`/api/embeddable/underwriting-history/${moovAccountId}`,
|
|
615
|
+
{
|
|
616
|
+
method: "GET",
|
|
617
|
+
}
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Find operator from Enverus
|
|
623
|
+
*
|
|
624
|
+
* @param {string|number} [opOrgId] - Optional Operator Org ID
|
|
625
|
+
* @param {string} [orgNumber] - Optional Org Number
|
|
626
|
+
* @returns {Promise<any>}
|
|
627
|
+
*/
|
|
628
|
+
async findOperatorFromEnverus(opOrgId = null, orgNumber = null) {
|
|
629
|
+
const params = new URLSearchParams();
|
|
630
|
+
if (opOrgId) params.append("opOrgId", opOrgId);
|
|
631
|
+
if (orgNumber) params.append("orgNumber", orgNumber);
|
|
632
|
+
|
|
633
|
+
const queryString = params.toString() ? `?${params.toString()}` : "";
|
|
634
|
+
|
|
635
|
+
return this.request(`/api/enverus/operators/lookup${queryString}`, {
|
|
636
|
+
method: "GET",
|
|
637
|
+
});
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
/**
|
|
641
|
+
* List all bank accounts for an operator
|
|
642
|
+
*
|
|
643
|
+
* @param {string} operatorId - The internal GUID of the operator
|
|
644
|
+
* @returns {Promise<any>}
|
|
645
|
+
*/
|
|
646
|
+
async getOperatorBankAccounts(operatorId) {
|
|
647
|
+
if (!operatorId) {
|
|
648
|
+
throw {
|
|
649
|
+
status: 400,
|
|
650
|
+
data: {
|
|
651
|
+
success: false,
|
|
652
|
+
message: "Operator ID is required",
|
|
653
|
+
errors: ["operatorId parameter is missing"],
|
|
654
|
+
},
|
|
655
|
+
};
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
return this.request(`/api/operators/${operatorId}/bank-accounts`, {
|
|
659
|
+
method: "GET",
|
|
660
|
+
});
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Add a new bank account for an operator
|
|
665
|
+
*
|
|
666
|
+
* @param {string} operatorId - The internal GUID of the operator
|
|
667
|
+
* @param {Object} bankAccountData - The bank account details to add
|
|
668
|
+
* @returns {Promise<any>}
|
|
669
|
+
*/
|
|
670
|
+
async addOperatorBankAccount(operatorId, bankAccountData) {
|
|
671
|
+
if (!operatorId) {
|
|
672
|
+
throw {
|
|
673
|
+
status: 400,
|
|
674
|
+
data: {
|
|
675
|
+
success: false,
|
|
676
|
+
message: "Operator ID is required",
|
|
677
|
+
errors: ["operatorId parameter is missing"],
|
|
678
|
+
},
|
|
679
|
+
};
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
return this.request(`/api/operators/${operatorId}/bank-accounts`, {
|
|
683
|
+
method: "POST",
|
|
684
|
+
body: JSON.stringify(bankAccountData),
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
/**
|
|
689
|
+
* Delete/unlink a bank account for an operator
|
|
690
|
+
*
|
|
691
|
+
* @param {string} operatorId - The internal GUID of the operator
|
|
692
|
+
* @param {string} bankAccountId - The ID of the bank account to delete
|
|
693
|
+
* @returns {Promise<any>}
|
|
694
|
+
*/
|
|
695
|
+
async deleteOperatorBankAccount(operatorId, bankAccountId) {
|
|
696
|
+
if (!operatorId || !bankAccountId) {
|
|
697
|
+
throw {
|
|
698
|
+
status: 400,
|
|
699
|
+
data: {
|
|
700
|
+
success: false,
|
|
701
|
+
message: "Operator ID and Bank Account ID are required",
|
|
702
|
+
errors: ["operatorId or bankAccountId parameter is missing"],
|
|
703
|
+
},
|
|
704
|
+
};
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
return this.request(`/api/operators/${operatorId}/bank-accounts/${bankAccountId}`, {
|
|
708
|
+
method: "DELETE",
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
// Export for ES6 modules (primary export method for modern bundlers)
|
|
714
|
+
export { BisonJibPayAPI };
|
|
715
|
+
|
|
716
|
+
// Make available globally for script tag usage
|
|
717
|
+
if (typeof window !== "undefined") {
|
|
718
|
+
window.BisonJibPayAPI = BisonJibPayAPI;
|
|
719
|
+
}
|
|
720
|
+
|
|
721
|
+
// Export for CommonJS (Node.js)
|
|
722
|
+
if (typeof module !== "undefined" && module.exports) {
|
|
723
|
+
module.exports = { BisonJibPayAPI };
|
|
724
|
+
}
|
|
725
|
+
|
|
93
726
|
class BisonOperatorPayments extends HTMLElement {
|
|
94
727
|
constructor() {
|
|
95
728
|
super();
|
|
@@ -153,7 +786,7 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
153
786
|
}
|
|
154
787
|
|
|
155
788
|
static get observedAttributes() {
|
|
156
|
-
return ["org-number", "op-org-id", "x-embeddable-key"];
|
|
789
|
+
return ["org-number", "op-org-id", "x-embeddable-key", "api-base-url"];
|
|
157
790
|
}
|
|
158
791
|
|
|
159
792
|
connectedCallback() {
|
|
@@ -202,13 +835,41 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
202
835
|
}
|
|
203
836
|
|
|
204
837
|
/** Resolves the API instance and syncs the current embeddable key. */
|
|
205
|
-
_getApi() {
|
|
206
|
-
|
|
207
|
-
this._api
|
|
208
|
-
|
|
209
|
-
|
|
838
|
+
async _getApi() {
|
|
839
|
+
if (this._api) {
|
|
840
|
+
if (this._embeddableKey) this._api.embeddableKey = this._embeddableKey;
|
|
841
|
+
return this._api;
|
|
842
|
+
}
|
|
843
|
+
if (typeof window !== "undefined" && window.__bisonApi) {
|
|
844
|
+
if (this._embeddableKey) window.__bisonApi.embeddableKey = this._embeddableKey;
|
|
845
|
+
return window.__bisonApi;
|
|
846
|
+
}
|
|
847
|
+
|
|
848
|
+
// Try reading config from window object explicitly provided by consumer
|
|
849
|
+
let baseUrl = this.getAttribute("api-base-url") || "";
|
|
850
|
+
let globalKey = this._embeddableKey;
|
|
851
|
+
if (typeof window !== "undefined" && window.BISON_JIB_PAY_CONFIG) {
|
|
852
|
+
baseUrl = baseUrl || window.BISON_JIB_PAY_CONFIG.apiBaseURL || "";
|
|
853
|
+
if (!this._embeddableKey && window.BISON_JIB_PAY_CONFIG.embeddableKey) {
|
|
854
|
+
globalKey = window.BISON_JIB_PAY_CONFIG.embeddableKey;
|
|
855
|
+
this._embeddableKey = globalKey; // Sync to component state
|
|
856
|
+
}
|
|
857
|
+
}
|
|
858
|
+
|
|
859
|
+
try {
|
|
860
|
+
this._api = new BisonJibPayAPI(baseUrl, globalKey);
|
|
861
|
+
|
|
862
|
+
// Attach to window to prevent redundant instantiations across components
|
|
863
|
+
if (typeof window !== "undefined") {
|
|
864
|
+
window.BisonJibPayAPI = BisonJibPayAPI;
|
|
865
|
+
window.__bisonApi = this._api;
|
|
866
|
+
}
|
|
867
|
+
|
|
868
|
+
return this._api;
|
|
869
|
+
} catch (err) {
|
|
870
|
+
this._log("Failed to initialize BisonJibPayAPI:", err);
|
|
871
|
+
return null;
|
|
210
872
|
}
|
|
211
|
-
return api;
|
|
212
873
|
}
|
|
213
874
|
|
|
214
875
|
// ==================== OPERATOR LOOKUP (Enverus) ====================
|
|
@@ -219,6 +880,12 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
219
880
|
* Priority: opOrgId > orgNumber
|
|
220
881
|
*/
|
|
221
882
|
_evaluateOperatorAttributes() {
|
|
883
|
+
// Try to resolve key from global config if not already set via attribute
|
|
884
|
+
if (!this._embeddableKey && typeof window !== "undefined" && window.BISON_JIB_PAY_CONFIG?.embeddableKey) {
|
|
885
|
+
this._embeddableKey = window.BISON_JIB_PAY_CONFIG.embeddableKey;
|
|
886
|
+
this._disabledReason = null;
|
|
887
|
+
}
|
|
888
|
+
|
|
222
889
|
// Gate: embeddable key must be present before any API calls
|
|
223
890
|
if (!this._embeddableKey) {
|
|
224
891
|
this._log("Embeddable key missing → skipping operator evaluation");
|
|
@@ -260,7 +927,7 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
260
927
|
async _performOperatorLookup(opOrgId, orgNumber) {
|
|
261
928
|
// Resolve API instance — consumers can set this._api externally,
|
|
262
929
|
// or the component falls back to a global instance on window.
|
|
263
|
-
const api = this._getApi();
|
|
930
|
+
const api = await this._getApi();
|
|
264
931
|
if (!api || typeof api.findOperatorFromEnverus !== "function") {
|
|
265
932
|
const msg =
|
|
266
933
|
"No API instance available — set component._api or window.__bisonApi";
|
|
@@ -280,8 +947,12 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
280
947
|
try {
|
|
281
948
|
const result = await api.findOperatorFromEnverus(opOrgId, orgNumber);
|
|
282
949
|
this._log("Operator lookup SUCCESS", result);
|
|
283
|
-
|
|
284
|
-
|
|
950
|
+
|
|
951
|
+
// Handle both wrapped and unwrapped response
|
|
952
|
+
const data = result?.data || result;
|
|
953
|
+
this._operatorData = data;
|
|
954
|
+
this._operatorId = data?.operatorId || null;
|
|
955
|
+
|
|
285
956
|
this._operatorLookupError = null;
|
|
286
957
|
this._componentDisabled = false;
|
|
287
958
|
this._dispatchLookupEvent({ status: "success", data: result });
|
|
@@ -334,6 +1005,8 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
334
1005
|
const loaderSvg =
|
|
335
1006
|
'<span class="bop-trigger-spinner"><svg viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-6.219-8.56"/></svg></span>';
|
|
336
1007
|
const logoImg = `<img src="${BOP_BISON_LOGO}" alt="Bison" class="bop-trigger-logo">`;
|
|
1008
|
+
|
|
1009
|
+
// Check for company name in operatorData
|
|
337
1010
|
const companyName = this._operatorData?.companyName || "";
|
|
338
1011
|
if (this._isOperatorLookupPending) {
|
|
339
1012
|
btn.innerHTML = `${loaderSvg} Initializing...`;
|
|
@@ -364,8 +1037,8 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
364
1037
|
this._log(`Fetching bank accounts for operatorId: ${this._operatorId}`);
|
|
365
1038
|
const response = await api.getOperatorBankAccounts(this._operatorId);
|
|
366
1039
|
|
|
367
|
-
//
|
|
368
|
-
const accountsData = response?.data || [];
|
|
1040
|
+
// Higher robustness: handle both direct array or wrapped in response.data
|
|
1041
|
+
const accountsData = Array.isArray(response) ? response : (response?.data || []);
|
|
369
1042
|
|
|
370
1043
|
// Map the DTO to the internal representation
|
|
371
1044
|
this._accounts = accountsData.map((acc) => ({
|
|
@@ -652,7 +1325,7 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
652
1325
|
this._isLoading = true;
|
|
653
1326
|
this._renderContent();
|
|
654
1327
|
|
|
655
|
-
const api = this._getApi();
|
|
1328
|
+
const api = await this._getApi();
|
|
656
1329
|
if (!api || !this._operatorId) {
|
|
657
1330
|
this._log("Error: Missing API instance or operator ID for unlinking.");
|
|
658
1331
|
this._isLoading = false;
|
|
@@ -892,7 +1565,7 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
892
1565
|
this._renderLinkModal();
|
|
893
1566
|
|
|
894
1567
|
try {
|
|
895
|
-
const api = this._getApi();
|
|
1568
|
+
const api = await this._getApi();
|
|
896
1569
|
if (!api || !this._operatorId) {
|
|
897
1570
|
throw new Error("Missing API instance or operator ID");
|
|
898
1571
|
}
|
|
@@ -915,25 +1588,25 @@ class BisonOperatorPayments extends HTMLElement {
|
|
|
915
1588
|
this._linkModalSubmitting = false;
|
|
916
1589
|
this._removeLinkModalGuards();
|
|
917
1590
|
|
|
918
|
-
// Store the new account as pending
|
|
919
|
-
const
|
|
920
|
-
const lastFour =
|
|
921
|
-
?
|
|
1591
|
+
// Store the new account as pending - handle both direct object or wrapped in response.data
|
|
1592
|
+
const data = response?.data || response || {};
|
|
1593
|
+
const lastFour = data.accountNumber
|
|
1594
|
+
? data.accountNumber.slice(-4)
|
|
922
1595
|
: this._linkModalValues.accountNumber.trim().slice(-4);
|
|
923
1596
|
|
|
924
1597
|
this._pendingLinkedAccount = {
|
|
925
|
-
id:
|
|
1598
|
+
id: data.id || `linked-${Date.now()}`,
|
|
926
1599
|
type:
|
|
927
|
-
|
|
1600
|
+
data.accountType === 0
|
|
928
1601
|
? "Checking"
|
|
929
|
-
:
|
|
1602
|
+
: data.accountType === 1
|
|
930
1603
|
? "Savings"
|
|
931
|
-
: payload.accountName || "Linked Account",
|
|
932
|
-
bankName:
|
|
1604
|
+
: data.accountName || payload.accountName || "Linked Account",
|
|
1605
|
+
bankName: data.bankName || payload.bankName,
|
|
933
1606
|
lastFour: lastFour.padStart(4, "0"),
|
|
934
1607
|
balance: 0,
|
|
935
|
-
isVerified:
|
|
936
|
-
isDefault:
|
|
1608
|
+
isVerified: data.isVerified || false,
|
|
1609
|
+
isDefault: data.isDefault || false,
|
|
937
1610
|
cannotUnlink: false,
|
|
938
1611
|
};
|
|
939
1612
|
|
|
@@ -2127,3 +2800,6 @@ if (typeof module !== "undefined" && module.exports) {
|
|
|
2127
2800
|
if (typeof window !== "undefined") {
|
|
2128
2801
|
window.BisonOperatorPayments = BisonOperatorPayments;
|
|
2129
2802
|
}
|
|
2803
|
+
|
|
2804
|
+
// Named export for ESM
|
|
2805
|
+
export { BisonOperatorPayments };
|