← back to Costa Rica
lib/plaid.js
53 lines
'use strict';
// Plaid client — bank-account verification + ACH for FOREIGN/US hosts only.
// (Costa Rican banks are NOT in Plaid's network — Tico hosts use SINPE, see
// lib/payouts.js.) Sandbox-safe: no PLAID creds => liveMode=false and Link
// token / exchange return deterministic sandbox values.
//
// Env (gated): PLAID_CLIENT_ID, PLAID_SECRET, PLAID_ENV (sandbox|production)
const crypto = require('crypto');
const { fetchT } = require('./payments/http'); // bound every live Plaid call (no infinite hang)
const CLIENT_ID = process.env.PLAID_CLIENT_ID || '';
const SECRET = process.env.PLAID_SECRET || '';
const ENV = process.env.PLAID_ENV || 'sandbox';
const LIVE = !!(CLIENT_ID && SECRET);
const BASE = `https://${ENV}.plaid.com`;
async function _post(path, body) {
// fetchT bounds connect+headers AND the body read (shared with tilopay/onvo) so a
// hung Plaid connection during host bank-linking can't leave the request pending
// forever — closes the last unbounded live provider fetch (PRE-FLIGHT #4/#5 class).
const res = await fetchT(BASE + path, {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ client_id: CLIENT_ID, secret: SECRET, ...body }),
});
const j = await res.json();
if (!res.ok) throw new Error(`plaid ${path} HTTP ${res.status}: ${j.error_code || ''}`);
return j;
}
// Step 1 (app): create a Link token to open Plaid Link in the app.
async function createLinkToken(userId) {
if (!LIVE) return { link_token: `link-sandbox-${crypto.randomBytes(6).toString('hex')}`, sandbox: true };
return _post('/link/token/create', {
user: { client_user_id: String(userId) },
client_name: 'Costa Rica Marketplace',
products: ['auth'], country_codes: ['US'], language: 'en',
});
}
// Step 2 (app -> server): exchange the public_token for a persistent access_token.
async function exchangePublicToken(publicToken) {
if (!LIVE) return { access_token: `access-sandbox-${crypto.randomBytes(8).toString('hex')}`, item_id: `item-${crypto.randomBytes(6).toString('hex')}`, sandbox: true };
return _post('/item/public_token/exchange', { public_token: publicToken });
}
// Fetch the ACH numbers/account so we can store last4 + verified flag.
async function getAuth(accessToken) {
if (!LIVE) return { accounts: [{ account_id: 'acc_sbx', mask: '4321' }], sandbox: true };
return _post('/auth/get', { access_token: accessToken });
}
module.exports = { get liveMode() { return LIVE; }, ENV, createLinkToken, exchangePublicToken, getAuth };