paybridge 0.6.0 → 0.8.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 +11 -0
- package/dist/cli/commands/providers.d.ts +1 -0
- package/dist/cli/commands/providers.js +135 -0
- package/dist/cli/commands/quote.d.ts +1 -0
- package/dist/cli/commands/quote.js +91 -0
- package/dist/cli/commands/test.d.ts +1 -0
- package/dist/cli/commands/test.js +92 -0
- package/dist/cli/commands/webhook.d.ts +1 -0
- package/dist/cli/commands/webhook.js +197 -0
- package/dist/cli/index.d.ts +2 -0
- package/dist/cli/index.js +45 -0
- package/dist/cli/runners.d.ts +10 -0
- package/dist/cli/runners.js +335 -0
- package/dist/cli/utils.d.ts +16 -0
- package/dist/cli/utils.js +98 -0
- package/package.json +11 -4
package/README.md
CHANGED
|
@@ -48,6 +48,17 @@ The playground lets you:
|
|
|
48
48
|
|
|
49
49
|
Perfect for demos, learning, and rapid prototyping. See [playground/README.md](playground/README.md) for details.
|
|
50
50
|
|
|
51
|
+
## Framework Examples
|
|
52
|
+
|
|
53
|
+
Runnable integrations for common Node.js frameworks:
|
|
54
|
+
|
|
55
|
+
- [Express](examples/frameworks/express/) — classic, raw body parsing for webhooks
|
|
56
|
+
- [Fastify](examples/frameworks/fastify/) — Fastify plugin pattern, custom content type parser
|
|
57
|
+
- [Next.js](examples/frameworks/nextjs/) — App Router API routes, multi-provider router
|
|
58
|
+
- [Hono](examples/frameworks/hono/) — edge-runtime ready (Cloudflare Workers, Bun, Deno, Node)
|
|
59
|
+
|
|
60
|
+
Each example uses `PayBridgeRouter` with Stripe + PayStack and demonstrates webhook signature verification, idempotency, and provider-specific routing.
|
|
61
|
+
|
|
51
62
|
## Quick Start
|
|
52
63
|
|
|
53
64
|
> **Upgrading from 0.1 or 0.2?** See [docs/migration.md](docs/migration.md).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runProviders(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runProviders = runProviders;
|
|
4
|
+
const index_1 = require("../../index");
|
|
5
|
+
const crypto_1 = require("../../crypto");
|
|
6
|
+
const utils_1 = require("../utils");
|
|
7
|
+
const FIAT_PROVIDERS = [
|
|
8
|
+
'softycomp',
|
|
9
|
+
'yoco',
|
|
10
|
+
'ozow',
|
|
11
|
+
'payfast',
|
|
12
|
+
'paystack',
|
|
13
|
+
'stripe',
|
|
14
|
+
'peach',
|
|
15
|
+
'flutterwave',
|
|
16
|
+
'adyen',
|
|
17
|
+
'mercadopago',
|
|
18
|
+
'razorpay',
|
|
19
|
+
'mollie',
|
|
20
|
+
'square',
|
|
21
|
+
'pesapal',
|
|
22
|
+
];
|
|
23
|
+
const CRYPTO_PROVIDERS = ['moonpay', 'yellowcard', 'transak', 'ramp'];
|
|
24
|
+
function createDummyProvider(name) {
|
|
25
|
+
const dummyCreds = {
|
|
26
|
+
softycomp: { apiKey: 'dummy', secretKey: 'dummy' },
|
|
27
|
+
yoco: { apiKey: 'dummy' },
|
|
28
|
+
ozow: { apiKey: 'dummy', siteCode: 'dummy', privateKey: 'dummy' },
|
|
29
|
+
payfast: { merchantId: 'dummy', merchantKey: 'dummy' },
|
|
30
|
+
paystack: { apiKey: 'dummy' },
|
|
31
|
+
stripe: { apiKey: 'dummy' },
|
|
32
|
+
peach: { apiKey: 'dummy', secretKey: 'dummy' },
|
|
33
|
+
flutterwave: { apiKey: 'dummy' },
|
|
34
|
+
adyen: { apiKey: 'dummy', merchantAccount: 'dummy' },
|
|
35
|
+
mercadopago: { apiKey: 'dummy' },
|
|
36
|
+
razorpay: { apiKey: 'dummy', secretKey: 'dummy' },
|
|
37
|
+
mollie: { apiKey: 'dummy' },
|
|
38
|
+
square: { apiKey: 'dummy', locationId: 'dummy' },
|
|
39
|
+
pesapal: { apiKey: 'dummy', secretKey: 'dummy' },
|
|
40
|
+
};
|
|
41
|
+
return new index_1.PayBridge({
|
|
42
|
+
provider: name,
|
|
43
|
+
credentials: dummyCreds[name],
|
|
44
|
+
sandbox: true,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
function createDummyCryptoProvider(name) {
|
|
48
|
+
const dummyCreds = {
|
|
49
|
+
moonpay: { apiKey: 'dummy', secretKey: 'dummy' },
|
|
50
|
+
yellowcard: { apiKey: 'dummy', secretKey: 'dummy' },
|
|
51
|
+
transak: { apiKey: 'dummy', secretKey: 'dummy' },
|
|
52
|
+
ramp: { apiKey: 'dummy' },
|
|
53
|
+
mock: { apiKey: 'dummy' },
|
|
54
|
+
};
|
|
55
|
+
return new crypto_1.CryptoRamp({
|
|
56
|
+
provider: name,
|
|
57
|
+
credentials: dummyCreds[name],
|
|
58
|
+
sandbox: true,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
async function runProviders(args) {
|
|
62
|
+
const isJson = args.includes('--json');
|
|
63
|
+
if (isJson) {
|
|
64
|
+
const fiat = FIAT_PROVIDERS.map((name) => {
|
|
65
|
+
const provider = createDummyProvider(name);
|
|
66
|
+
const caps = provider.provider.getCapabilities();
|
|
67
|
+
return {
|
|
68
|
+
provider: name,
|
|
69
|
+
type: 'fiat',
|
|
70
|
+
region: caps.country || 'N/A',
|
|
71
|
+
currencies: caps.currencies,
|
|
72
|
+
fee: `${caps.fees.percent ?? 0}% + ${caps.fees.fixed ?? 0}`,
|
|
73
|
+
minAmount: caps.minAmount,
|
|
74
|
+
maxAmount: caps.maxAmount,
|
|
75
|
+
avgLatencyMs: caps.avgLatencyMs,
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
const crypto = CRYPTO_PROVIDERS.map((name) => {
|
|
79
|
+
const ramp = createDummyCryptoProvider(name);
|
|
80
|
+
const caps = ramp.getCapabilities();
|
|
81
|
+
return {
|
|
82
|
+
provider: name,
|
|
83
|
+
type: 'crypto',
|
|
84
|
+
onRampSupported: !!caps.onRampLimits,
|
|
85
|
+
offRampSupported: !!caps.offRampLimits,
|
|
86
|
+
assets: caps.supportedAssets,
|
|
87
|
+
onRampFeePercent: caps.fees.onRampPercent,
|
|
88
|
+
offRampFeePercent: caps.fees.offRampPercent,
|
|
89
|
+
experimental: caps.experimental,
|
|
90
|
+
avgLatencyMs: caps.avgLatencyMs,
|
|
91
|
+
};
|
|
92
|
+
});
|
|
93
|
+
console.log(JSON.stringify({ fiat, crypto }, null, 2));
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
console.log((0, utils_1.colorize)('\nFIAT PROVIDERS', 'bright'));
|
|
97
|
+
console.log('==============\n');
|
|
98
|
+
const fiatRows = [
|
|
99
|
+
['PROVIDER', 'REGION', 'CURRENCIES', 'FEE'],
|
|
100
|
+
];
|
|
101
|
+
for (const name of FIAT_PROVIDERS) {
|
|
102
|
+
const provider = createDummyProvider(name);
|
|
103
|
+
const caps = provider.provider.getCapabilities();
|
|
104
|
+
fiatRows.push([
|
|
105
|
+
name,
|
|
106
|
+
caps.country || 'N/A',
|
|
107
|
+
caps.currencies.join('/'),
|
|
108
|
+
`${caps.fees.percent ?? 0}% + ${caps.fees.fixed ?? 0}`,
|
|
109
|
+
]);
|
|
110
|
+
}
|
|
111
|
+
console.log((0, utils_1.formatTable)(fiatRows));
|
|
112
|
+
console.log((0, utils_1.colorize)('\n\nCRYPTO PROVIDERS', 'bright'));
|
|
113
|
+
console.log('================\n');
|
|
114
|
+
const cryptoRows = [
|
|
115
|
+
['PROVIDER', 'ON-RAMP', 'OFF-RAMP', 'ASSETS', 'FEE (ON/OFF)'],
|
|
116
|
+
];
|
|
117
|
+
for (const name of CRYPTO_PROVIDERS) {
|
|
118
|
+
const ramp = createDummyCryptoProvider(name);
|
|
119
|
+
const caps = ramp.getCapabilities();
|
|
120
|
+
const onRampIcon = caps.experimental ? '⚠' : caps.onRampLimits ? '✓' : '✗';
|
|
121
|
+
const offRampIcon = caps.experimental ? '⚠' : caps.offRampLimits ? '✓' : '✗';
|
|
122
|
+
const feeText = caps.experimental
|
|
123
|
+
? '⚠ experimental'
|
|
124
|
+
: `${caps.fees.onRampPercent ?? 0}% / ${caps.fees.offRampPercent ?? 0}%`;
|
|
125
|
+
cryptoRows.push([
|
|
126
|
+
name,
|
|
127
|
+
onRampIcon,
|
|
128
|
+
offRampIcon,
|
|
129
|
+
caps.supportedAssets.slice(0, 4).join('/'),
|
|
130
|
+
feeText,
|
|
131
|
+
]);
|
|
132
|
+
}
|
|
133
|
+
console.log((0, utils_1.formatTable)(cryptoRows));
|
|
134
|
+
console.log('');
|
|
135
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runQuote(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runQuote = runQuote;
|
|
4
|
+
const crypto_1 = require("../../crypto");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const CRYPTO_PROVIDERS = ['moonpay', 'yellowcard', 'transak', 'ramp'];
|
|
7
|
+
function createCryptoProviderFromEnv(providerName) {
|
|
8
|
+
const envMap = {
|
|
9
|
+
moonpay: {
|
|
10
|
+
keys: ['MOONPAY_API_KEY', 'MOONPAY_SECRET_KEY'],
|
|
11
|
+
mapper: (env) => ({ apiKey: env.MOONPAY_API_KEY, secretKey: env.MOONPAY_SECRET_KEY }),
|
|
12
|
+
},
|
|
13
|
+
yellowcard: {
|
|
14
|
+
keys: ['YELLOWCARD_API_KEY', 'YELLOWCARD_SECRET_KEY'],
|
|
15
|
+
mapper: (env) => ({ apiKey: env.YELLOWCARD_API_KEY, secretKey: env.YELLOWCARD_SECRET_KEY }),
|
|
16
|
+
},
|
|
17
|
+
transak: {
|
|
18
|
+
keys: ['TRANSAK_API_KEY', 'TRANSAK_SECRET_KEY'],
|
|
19
|
+
mapper: (env) => ({ apiKey: env.TRANSAK_API_KEY, secretKey: env.TRANSAK_SECRET_KEY }),
|
|
20
|
+
},
|
|
21
|
+
ramp: {
|
|
22
|
+
keys: ['RAMP_API_KEY'],
|
|
23
|
+
mapper: (env) => ({ apiKey: env.RAMP_API_KEY }),
|
|
24
|
+
},
|
|
25
|
+
mock: {
|
|
26
|
+
keys: [],
|
|
27
|
+
mapper: () => ({ apiKey: 'mock' }),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
const config = envMap[providerName];
|
|
31
|
+
if (!config) {
|
|
32
|
+
throw new Error(`Unknown crypto provider: ${providerName}`);
|
|
33
|
+
}
|
|
34
|
+
const missing = config.keys.filter((k) => !process.env[k]);
|
|
35
|
+
if (missing.length > 0) {
|
|
36
|
+
throw new Error(`Missing required env vars: ${missing.join(', ')}`);
|
|
37
|
+
}
|
|
38
|
+
const credentials = config.mapper(process.env);
|
|
39
|
+
return new crypto_1.CryptoRamp({
|
|
40
|
+
provider: providerName,
|
|
41
|
+
credentials,
|
|
42
|
+
sandbox: true,
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function runQuote(args) {
|
|
46
|
+
const providerName = args[0];
|
|
47
|
+
if (!providerName) {
|
|
48
|
+
console.error('Usage: paybridge quote <provider> --direction on|off --fiat-amount N --fiat-currency CUR --asset ASSET --network NET');
|
|
49
|
+
process.exit(1);
|
|
50
|
+
}
|
|
51
|
+
if (!CRYPTO_PROVIDERS.includes(providerName)) {
|
|
52
|
+
console.error(`Unknown crypto provider: ${providerName}`);
|
|
53
|
+
console.error(`Available: ${CRYPTO_PROVIDERS.join(', ')}`);
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
const getArg = (flag) => {
|
|
57
|
+
const idx = args.indexOf(flag);
|
|
58
|
+
return idx >= 0 && args[idx + 1] ? args[idx + 1] : undefined;
|
|
59
|
+
};
|
|
60
|
+
const direction = getArg('--direction');
|
|
61
|
+
const fiatAmountStr = getArg('--fiat-amount');
|
|
62
|
+
const fiatCurrency = getArg('--fiat-currency');
|
|
63
|
+
const asset = getArg('--asset');
|
|
64
|
+
const network = getArg('--network');
|
|
65
|
+
if (!direction || !fiatAmountStr || !fiatCurrency || !asset) {
|
|
66
|
+
console.error('Missing required flags: --direction, --fiat-amount, --fiat-currency, --asset');
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
69
|
+
const fiatAmount = parseFloat(fiatAmountStr);
|
|
70
|
+
if (!Number.isFinite(fiatAmount) || fiatAmount <= 0) {
|
|
71
|
+
console.error('Invalid fiat amount (must be positive number)');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
let provider;
|
|
75
|
+
try {
|
|
76
|
+
provider = createCryptoProviderFromEnv(providerName);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
console.error((0, utils_1.colorize)(error.message, 'red'));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
try {
|
|
83
|
+
const quote = await provider.getQuote(direction, fiatAmount, fiatCurrency, asset, network || asset);
|
|
84
|
+
console.log(JSON.stringify(quote, null, 2));
|
|
85
|
+
process.exit(0);
|
|
86
|
+
}
|
|
87
|
+
catch (error) {
|
|
88
|
+
console.error((0, utils_1.colorize)(`Quote error: ${error.message}`, 'red'));
|
|
89
|
+
process.exit(1);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runTest(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runTest = runTest;
|
|
4
|
+
const runners_1 = require("../runners");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
async function runProviderTest(runner) {
|
|
7
|
+
const missing = runner.envRequired.filter((key) => !process.env[key]);
|
|
8
|
+
if (missing.length > 0) {
|
|
9
|
+
return {
|
|
10
|
+
provider: runner.name,
|
|
11
|
+
status: 'skipped',
|
|
12
|
+
message: `Missing: ${missing.join(', ')}`,
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
console.log(`${(0, utils_1.colorize)('[…]', 'cyan')} ${runner.name} - validating...`);
|
|
16
|
+
try {
|
|
17
|
+
const result = await runner.run();
|
|
18
|
+
if (!result.id) {
|
|
19
|
+
throw new Error('No payment ID returned');
|
|
20
|
+
}
|
|
21
|
+
console.log(`${(0, utils_1.colorize)('[✓]', 'green')} ${runner.name} → id=${result.id}${result.checkoutUrl ? `, url=${result.checkoutUrl.substring(0, 60)}...` : ''}, status=${result.status}`);
|
|
22
|
+
return {
|
|
23
|
+
provider: runner.name,
|
|
24
|
+
status: 'success',
|
|
25
|
+
data: {
|
|
26
|
+
id: result.id,
|
|
27
|
+
checkoutUrl: result.checkoutUrl,
|
|
28
|
+
status: result.status,
|
|
29
|
+
},
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
console.log(`${(0, utils_1.colorize)('[✗]', 'red')} ${runner.name} ERROR: ${error.message || String(error)}`);
|
|
34
|
+
return {
|
|
35
|
+
provider: runner.name,
|
|
36
|
+
status: 'failed',
|
|
37
|
+
message: error.message || String(error),
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
async function runTest(args) {
|
|
42
|
+
const isAll = args.includes('--all');
|
|
43
|
+
const providerName = args.find((a) => !a.startsWith('--'));
|
|
44
|
+
if (!isAll && !providerName) {
|
|
45
|
+
console.error('Usage: paybridge test <provider> | paybridge test --all');
|
|
46
|
+
process.exit(1);
|
|
47
|
+
}
|
|
48
|
+
console.log('\n=== PayBridge Test ===\n');
|
|
49
|
+
const results = [];
|
|
50
|
+
if (isAll) {
|
|
51
|
+
for (const runner of runners_1.runners) {
|
|
52
|
+
const result = await runProviderTest(runner);
|
|
53
|
+
results.push(result);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
const runner = runners_1.runners.find((r) => r.name === providerName);
|
|
58
|
+
if (!runner) {
|
|
59
|
+
console.error(`Unknown provider: ${providerName}`);
|
|
60
|
+
console.error(`Available: ${runners_1.runners.map((r) => r.name).join(', ')}`);
|
|
61
|
+
process.exit(1);
|
|
62
|
+
}
|
|
63
|
+
const result = await runProviderTest(runner);
|
|
64
|
+
results.push(result);
|
|
65
|
+
}
|
|
66
|
+
console.log('\n=== Summary ===\n');
|
|
67
|
+
const succeeded = results.filter((r) => r.status === 'success').length;
|
|
68
|
+
const skipped = results.filter((r) => r.status === 'skipped').length;
|
|
69
|
+
const failed = results.filter((r) => r.status === 'failed').length;
|
|
70
|
+
results.forEach((r) => {
|
|
71
|
+
const icon = r.status === 'success'
|
|
72
|
+
? (0, utils_1.colorize)('[✓]', 'green')
|
|
73
|
+
: r.status === 'skipped'
|
|
74
|
+
? (0, utils_1.colorize)('[ ]', 'dim')
|
|
75
|
+
: (0, utils_1.colorize)('[✗]', 'red');
|
|
76
|
+
const msg = r.message ? ` (${r.message})` : '';
|
|
77
|
+
console.log(`${icon} ${r.provider}${msg}`);
|
|
78
|
+
});
|
|
79
|
+
console.log(`\nSucceeded: ${succeeded}, Skipped: ${skipped}, Failed: ${failed}`);
|
|
80
|
+
if (failed > 0) {
|
|
81
|
+
console.log('\nSome providers failed validation.');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
else if (succeeded === 0) {
|
|
85
|
+
console.log('\nNo providers validated (all skipped). Set env vars to validate.');
|
|
86
|
+
process.exit(0);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
console.log('\nAll enabled providers validated successfully!');
|
|
90
|
+
process.exit(0);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function runWebhook(args: string[]): Promise<void>;
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runWebhook = runWebhook;
|
|
4
|
+
const index_1 = require("../../index");
|
|
5
|
+
const utils_1 = require("../utils");
|
|
6
|
+
const PROVIDERS = [
|
|
7
|
+
'softycomp',
|
|
8
|
+
'yoco',
|
|
9
|
+
'ozow',
|
|
10
|
+
'payfast',
|
|
11
|
+
'paystack',
|
|
12
|
+
'stripe',
|
|
13
|
+
'peach',
|
|
14
|
+
'flutterwave',
|
|
15
|
+
'adyen',
|
|
16
|
+
'mercadopago',
|
|
17
|
+
'razorpay',
|
|
18
|
+
'mollie',
|
|
19
|
+
'square',
|
|
20
|
+
'pesapal',
|
|
21
|
+
];
|
|
22
|
+
function createProviderFromEnv(providerName) {
|
|
23
|
+
const envMap = {
|
|
24
|
+
softycomp: {
|
|
25
|
+
keys: ['SOFTYCOMP_API_KEY', 'SOFTYCOMP_SECRET_KEY', 'SOFTYCOMP_WEBHOOK_SECRET'],
|
|
26
|
+
mapper: (env) => ({
|
|
27
|
+
apiKey: env.SOFTYCOMP_API_KEY,
|
|
28
|
+
secretKey: env.SOFTYCOMP_SECRET_KEY,
|
|
29
|
+
webhookSecret: env.SOFTYCOMP_WEBHOOK_SECRET,
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
yoco: {
|
|
33
|
+
keys: ['YOCO_API_KEY', 'YOCO_WEBHOOK_SECRET'],
|
|
34
|
+
mapper: (env) => ({ apiKey: env.YOCO_API_KEY, webhookSecret: env.YOCO_WEBHOOK_SECRET }),
|
|
35
|
+
},
|
|
36
|
+
ozow: {
|
|
37
|
+
keys: ['OZOW_API_KEY', 'OZOW_SITE_CODE', 'OZOW_PRIVATE_KEY'],
|
|
38
|
+
mapper: (env) => ({
|
|
39
|
+
apiKey: env.OZOW_API_KEY,
|
|
40
|
+
siteCode: env.OZOW_SITE_CODE,
|
|
41
|
+
privateKey: env.OZOW_PRIVATE_KEY,
|
|
42
|
+
}),
|
|
43
|
+
},
|
|
44
|
+
payfast: {
|
|
45
|
+
keys: ['PAYFAST_MERCHANT_ID', 'PAYFAST_MERCHANT_KEY', 'PAYFAST_PASSPHRASE'],
|
|
46
|
+
mapper: (env) => ({
|
|
47
|
+
merchantId: env.PAYFAST_MERCHANT_ID,
|
|
48
|
+
merchantKey: env.PAYFAST_MERCHANT_KEY,
|
|
49
|
+
passphrase: env.PAYFAST_PASSPHRASE,
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
52
|
+
paystack: {
|
|
53
|
+
keys: ['PAYSTACK_API_KEY', 'PAYSTACK_WEBHOOK_SECRET'],
|
|
54
|
+
mapper: (env) => ({ apiKey: env.PAYSTACK_API_KEY, webhookSecret: env.PAYSTACK_WEBHOOK_SECRET }),
|
|
55
|
+
},
|
|
56
|
+
stripe: {
|
|
57
|
+
keys: ['STRIPE_API_KEY', 'STRIPE_WEBHOOK_SECRET'],
|
|
58
|
+
mapper: (env) => ({ apiKey: env.STRIPE_API_KEY, webhookSecret: env.STRIPE_WEBHOOK_SECRET }),
|
|
59
|
+
},
|
|
60
|
+
peach: {
|
|
61
|
+
keys: ['PEACH_ACCESS_TOKEN', 'PEACH_ENTITY_ID', 'PEACH_WEBHOOK_SECRET'],
|
|
62
|
+
mapper: (env) => ({
|
|
63
|
+
apiKey: env.PEACH_ACCESS_TOKEN,
|
|
64
|
+
secretKey: env.PEACH_ENTITY_ID,
|
|
65
|
+
webhookSecret: env.PEACH_WEBHOOK_SECRET,
|
|
66
|
+
}),
|
|
67
|
+
},
|
|
68
|
+
flutterwave: {
|
|
69
|
+
keys: ['FLUTTERWAVE_API_KEY', 'FLUTTERWAVE_WEBHOOK_SECRET'],
|
|
70
|
+
mapper: (env) => ({ apiKey: env.FLUTTERWAVE_API_KEY, webhookSecret: env.FLUTTERWAVE_WEBHOOK_SECRET }),
|
|
71
|
+
},
|
|
72
|
+
adyen: {
|
|
73
|
+
keys: ['ADYEN_API_KEY', 'ADYEN_MERCHANT_ACCOUNT', 'ADYEN_WEBHOOK_SECRET'],
|
|
74
|
+
mapper: (env) => ({
|
|
75
|
+
apiKey: env.ADYEN_API_KEY,
|
|
76
|
+
merchantAccount: env.ADYEN_MERCHANT_ACCOUNT,
|
|
77
|
+
webhookSecret: env.ADYEN_WEBHOOK_SECRET,
|
|
78
|
+
}),
|
|
79
|
+
},
|
|
80
|
+
mercadopago: {
|
|
81
|
+
keys: ['MERCADOPAGO_ACCESS_TOKEN', 'MERCADOPAGO_WEBHOOK_SECRET'],
|
|
82
|
+
mapper: (env) => ({ apiKey: env.MERCADOPAGO_ACCESS_TOKEN, webhookSecret: env.MERCADOPAGO_WEBHOOK_SECRET }),
|
|
83
|
+
},
|
|
84
|
+
razorpay: {
|
|
85
|
+
keys: ['RAZORPAY_KEY_ID', 'RAZORPAY_KEY_SECRET', 'RAZORPAY_WEBHOOK_SECRET'],
|
|
86
|
+
mapper: (env) => ({
|
|
87
|
+
apiKey: env.RAZORPAY_KEY_ID,
|
|
88
|
+
secretKey: env.RAZORPAY_KEY_SECRET,
|
|
89
|
+
webhookSecret: env.RAZORPAY_WEBHOOK_SECRET,
|
|
90
|
+
}),
|
|
91
|
+
},
|
|
92
|
+
mollie: {
|
|
93
|
+
keys: ['MOLLIE_API_KEY'],
|
|
94
|
+
mapper: (env) => ({ apiKey: env.MOLLIE_API_KEY }),
|
|
95
|
+
},
|
|
96
|
+
square: {
|
|
97
|
+
keys: ['SQUARE_ACCESS_TOKEN', 'SQUARE_LOCATION_ID', 'SQUARE_WEBHOOK_SECRET'],
|
|
98
|
+
mapper: (env) => ({
|
|
99
|
+
apiKey: env.SQUARE_ACCESS_TOKEN,
|
|
100
|
+
locationId: env.SQUARE_LOCATION_ID,
|
|
101
|
+
webhookSecret: env.SQUARE_WEBHOOK_SECRET,
|
|
102
|
+
}),
|
|
103
|
+
},
|
|
104
|
+
pesapal: {
|
|
105
|
+
keys: ['PESAPAL_CONSUMER_KEY', 'PESAPAL_CONSUMER_SECRET'],
|
|
106
|
+
mapper: (env) => ({ apiKey: env.PESAPAL_CONSUMER_KEY, secretKey: env.PESAPAL_CONSUMER_SECRET }),
|
|
107
|
+
},
|
|
108
|
+
};
|
|
109
|
+
const config = envMap[providerName];
|
|
110
|
+
if (!config) {
|
|
111
|
+
throw new Error(`Unknown provider: ${providerName}`);
|
|
112
|
+
}
|
|
113
|
+
const required = config.keys.filter((k) => !k.includes('WEBHOOK_SECRET'));
|
|
114
|
+
const missing = required.filter((k) => !process.env[k]);
|
|
115
|
+
if (missing.length > 0) {
|
|
116
|
+
throw new Error(`Missing required env vars: ${missing.join(', ')}`);
|
|
117
|
+
}
|
|
118
|
+
const credentials = config.mapper(process.env);
|
|
119
|
+
return new index_1.PayBridge({
|
|
120
|
+
provider: providerName,
|
|
121
|
+
credentials,
|
|
122
|
+
sandbox: true,
|
|
123
|
+
webhookSecret: credentials.webhookSecret,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
async function runWebhook(args) {
|
|
127
|
+
const subcommand = args[0];
|
|
128
|
+
const providerName = args[1];
|
|
129
|
+
if (!subcommand || !providerName) {
|
|
130
|
+
console.error('Usage: paybridge webhook verify|parse <provider> [--header key=value]');
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
if (!PROVIDERS.includes(providerName)) {
|
|
134
|
+
console.error(`Unknown provider: ${providerName}`);
|
|
135
|
+
console.error(`Available: ${PROVIDERS.join(', ')}`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
const headerFlags = args.slice(2).filter((a) => a.startsWith('--header')).map((a) => a.replace('--header', '').trim());
|
|
139
|
+
const headersJsonFlag = args.find((a) => a.startsWith('--headers-json='));
|
|
140
|
+
let headers = {};
|
|
141
|
+
if (headersJsonFlag) {
|
|
142
|
+
const json = headersJsonFlag.replace('--headers-json=', '');
|
|
143
|
+
try {
|
|
144
|
+
headers = JSON.parse(json);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
console.error('Invalid JSON in --headers-json');
|
|
148
|
+
process.exit(1);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
headers = (0, utils_1.parseHeaders)(headerFlags);
|
|
153
|
+
}
|
|
154
|
+
const rawBody = await (0, utils_1.readStdin)();
|
|
155
|
+
let body;
|
|
156
|
+
try {
|
|
157
|
+
body = JSON.parse(rawBody);
|
|
158
|
+
}
|
|
159
|
+
catch {
|
|
160
|
+
body = rawBody;
|
|
161
|
+
}
|
|
162
|
+
let provider;
|
|
163
|
+
try {
|
|
164
|
+
provider = createProviderFromEnv(providerName);
|
|
165
|
+
}
|
|
166
|
+
catch (error) {
|
|
167
|
+
console.error((0, utils_1.colorize)(error.message, 'red'));
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
if (subcommand === 'verify') {
|
|
171
|
+
const valid = provider.verifyWebhook(body, headers);
|
|
172
|
+
if (valid) {
|
|
173
|
+
console.log((0, utils_1.colorize)('OK', 'green'));
|
|
174
|
+
process.exit(0);
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
console.log((0, utils_1.colorize)('INVALID', 'red'));
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
else if (subcommand === 'parse') {
|
|
182
|
+
try {
|
|
183
|
+
const event = provider.parseWebhook(body, headers);
|
|
184
|
+
console.log(JSON.stringify(event, null, 2));
|
|
185
|
+
process.exit(0);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
console.error((0, utils_1.colorize)(`Parse error: ${error.message}`, 'red'));
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
console.error(`Unknown webhook subcommand: ${subcommand}`);
|
|
194
|
+
console.error('Use "verify" or "parse"');
|
|
195
|
+
process.exit(1);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const test_1 = require("./commands/test");
|
|
5
|
+
const providers_1 = require("./commands/providers");
|
|
6
|
+
const webhook_1 = require("./commands/webhook");
|
|
7
|
+
const quote_1 = require("./commands/quote");
|
|
8
|
+
const utils_1 = require("./utils");
|
|
9
|
+
async function main() {
|
|
10
|
+
const [, , command, ...args] = process.argv;
|
|
11
|
+
switch (command) {
|
|
12
|
+
case 'test':
|
|
13
|
+
await (0, test_1.runTest)(args);
|
|
14
|
+
break;
|
|
15
|
+
case 'providers':
|
|
16
|
+
await (0, providers_1.runProviders)(args);
|
|
17
|
+
break;
|
|
18
|
+
case 'webhook':
|
|
19
|
+
await (0, webhook_1.runWebhook)(args);
|
|
20
|
+
break;
|
|
21
|
+
case 'quote':
|
|
22
|
+
await (0, quote_1.runQuote)(args);
|
|
23
|
+
break;
|
|
24
|
+
case '-h':
|
|
25
|
+
case '--help':
|
|
26
|
+
case 'help':
|
|
27
|
+
case undefined:
|
|
28
|
+
(0, utils_1.printHelp)();
|
|
29
|
+
process.exit(0);
|
|
30
|
+
break;
|
|
31
|
+
case '-v':
|
|
32
|
+
case '--version':
|
|
33
|
+
(0, utils_1.printVersion)();
|
|
34
|
+
process.exit(0);
|
|
35
|
+
break;
|
|
36
|
+
default:
|
|
37
|
+
console.error(`Unknown command: ${command}`);
|
|
38
|
+
(0, utils_1.printHelp)(true);
|
|
39
|
+
process.exit(1);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
main().catch((err) => {
|
|
43
|
+
console.error(err.message);
|
|
44
|
+
process.exit(1);
|
|
45
|
+
});
|
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.runners = void 0;
|
|
4
|
+
const index_1 = require("../index");
|
|
5
|
+
const crypto_1 = require("../crypto");
|
|
6
|
+
const timestamp = Date.now();
|
|
7
|
+
const testCustomer = {
|
|
8
|
+
name: 'Test User',
|
|
9
|
+
email: 'test@example.com',
|
|
10
|
+
phone: '0825551234',
|
|
11
|
+
};
|
|
12
|
+
const testUrls = {
|
|
13
|
+
success: 'https://example.com/success',
|
|
14
|
+
cancel: 'https://example.com/cancel',
|
|
15
|
+
webhook: 'https://example.com/webhook',
|
|
16
|
+
};
|
|
17
|
+
exports.runners = [
|
|
18
|
+
{
|
|
19
|
+
name: 'softycomp',
|
|
20
|
+
envRequired: ['SOFTYCOMP_API_KEY', 'SOFTYCOMP_SECRET_KEY'],
|
|
21
|
+
run: async () => {
|
|
22
|
+
const pay = new index_1.PayBridge({
|
|
23
|
+
provider: 'softycomp',
|
|
24
|
+
credentials: {
|
|
25
|
+
apiKey: process.env.SOFTYCOMP_API_KEY,
|
|
26
|
+
secretKey: process.env.SOFTYCOMP_SECRET_KEY,
|
|
27
|
+
},
|
|
28
|
+
sandbox: true,
|
|
29
|
+
});
|
|
30
|
+
const payment = await pay.createPayment({
|
|
31
|
+
amount: 1.0,
|
|
32
|
+
currency: 'ZAR',
|
|
33
|
+
reference: `cli-test-${timestamp}`,
|
|
34
|
+
customer: testCustomer,
|
|
35
|
+
urls: testUrls,
|
|
36
|
+
});
|
|
37
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
name: 'yoco',
|
|
42
|
+
envRequired: ['YOCO_API_KEY'],
|
|
43
|
+
run: async () => {
|
|
44
|
+
const pay = new index_1.PayBridge({
|
|
45
|
+
provider: 'yoco',
|
|
46
|
+
credentials: { apiKey: process.env.YOCO_API_KEY },
|
|
47
|
+
sandbox: true,
|
|
48
|
+
});
|
|
49
|
+
const payment = await pay.createPayment({
|
|
50
|
+
amount: 1.0,
|
|
51
|
+
currency: 'ZAR',
|
|
52
|
+
reference: `cli-test-${timestamp}`,
|
|
53
|
+
customer: testCustomer,
|
|
54
|
+
urls: testUrls,
|
|
55
|
+
});
|
|
56
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'ozow',
|
|
61
|
+
envRequired: ['OZOW_API_KEY', 'OZOW_SITE_CODE', 'OZOW_PRIVATE_KEY'],
|
|
62
|
+
run: async () => {
|
|
63
|
+
const pay = new index_1.PayBridge({
|
|
64
|
+
provider: 'ozow',
|
|
65
|
+
credentials: {
|
|
66
|
+
apiKey: process.env.OZOW_API_KEY,
|
|
67
|
+
siteCode: process.env.OZOW_SITE_CODE,
|
|
68
|
+
privateKey: process.env.OZOW_PRIVATE_KEY,
|
|
69
|
+
},
|
|
70
|
+
sandbox: true,
|
|
71
|
+
});
|
|
72
|
+
const payment = await pay.createPayment({
|
|
73
|
+
amount: 1.0,
|
|
74
|
+
currency: 'ZAR',
|
|
75
|
+
reference: `cli-test-${timestamp}`,
|
|
76
|
+
customer: testCustomer,
|
|
77
|
+
urls: testUrls,
|
|
78
|
+
});
|
|
79
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'payfast',
|
|
84
|
+
envRequired: ['PAYFAST_MERCHANT_ID', 'PAYFAST_MERCHANT_KEY'],
|
|
85
|
+
run: async () => {
|
|
86
|
+
const pay = new index_1.PayBridge({
|
|
87
|
+
provider: 'payfast',
|
|
88
|
+
credentials: {
|
|
89
|
+
merchantId: process.env.PAYFAST_MERCHANT_ID,
|
|
90
|
+
merchantKey: process.env.PAYFAST_MERCHANT_KEY,
|
|
91
|
+
passphrase: process.env.PAYFAST_PASSPHRASE,
|
|
92
|
+
},
|
|
93
|
+
sandbox: true,
|
|
94
|
+
});
|
|
95
|
+
const payment = await pay.createPayment({
|
|
96
|
+
amount: 1.0,
|
|
97
|
+
currency: 'ZAR',
|
|
98
|
+
reference: `cli-test-${timestamp}`,
|
|
99
|
+
customer: testCustomer,
|
|
100
|
+
urls: testUrls,
|
|
101
|
+
});
|
|
102
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'paystack',
|
|
107
|
+
envRequired: ['PAYSTACK_API_KEY'],
|
|
108
|
+
run: async () => {
|
|
109
|
+
const pay = new index_1.PayBridge({
|
|
110
|
+
provider: 'paystack',
|
|
111
|
+
credentials: { apiKey: process.env.PAYSTACK_API_KEY },
|
|
112
|
+
sandbox: true,
|
|
113
|
+
});
|
|
114
|
+
const payment = await pay.createPayment({
|
|
115
|
+
amount: 1.0,
|
|
116
|
+
currency: 'NGN',
|
|
117
|
+
reference: `cli-test-${timestamp}`,
|
|
118
|
+
customer: testCustomer,
|
|
119
|
+
urls: testUrls,
|
|
120
|
+
});
|
|
121
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
name: 'stripe',
|
|
126
|
+
envRequired: ['STRIPE_API_KEY'],
|
|
127
|
+
run: async () => {
|
|
128
|
+
const pay = new index_1.PayBridge({
|
|
129
|
+
provider: 'stripe',
|
|
130
|
+
credentials: { apiKey: process.env.STRIPE_API_KEY },
|
|
131
|
+
sandbox: true,
|
|
132
|
+
});
|
|
133
|
+
const payment = await pay.createPayment({
|
|
134
|
+
amount: 1.0,
|
|
135
|
+
currency: 'USD',
|
|
136
|
+
reference: `cli-test-${timestamp}`,
|
|
137
|
+
customer: testCustomer,
|
|
138
|
+
urls: testUrls,
|
|
139
|
+
});
|
|
140
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
name: 'peach',
|
|
145
|
+
envRequired: ['PEACH_ACCESS_TOKEN', 'PEACH_ENTITY_ID'],
|
|
146
|
+
run: async () => {
|
|
147
|
+
const pay = new index_1.PayBridge({
|
|
148
|
+
provider: 'peach',
|
|
149
|
+
credentials: {
|
|
150
|
+
apiKey: process.env.PEACH_ACCESS_TOKEN,
|
|
151
|
+
secretKey: process.env.PEACH_ENTITY_ID,
|
|
152
|
+
},
|
|
153
|
+
sandbox: true,
|
|
154
|
+
});
|
|
155
|
+
const payment = await pay.createPayment({
|
|
156
|
+
amount: 1.0,
|
|
157
|
+
currency: 'ZAR',
|
|
158
|
+
reference: `cli-test-${timestamp}`,
|
|
159
|
+
customer: testCustomer,
|
|
160
|
+
urls: testUrls,
|
|
161
|
+
});
|
|
162
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
name: 'flutterwave',
|
|
167
|
+
envRequired: ['FLUTTERWAVE_API_KEY'],
|
|
168
|
+
run: async () => {
|
|
169
|
+
const pay = new index_1.PayBridge({
|
|
170
|
+
provider: 'flutterwave',
|
|
171
|
+
credentials: { apiKey: process.env.FLUTTERWAVE_API_KEY },
|
|
172
|
+
sandbox: true,
|
|
173
|
+
});
|
|
174
|
+
const payment = await pay.createPayment({
|
|
175
|
+
amount: 1.0,
|
|
176
|
+
currency: 'NGN',
|
|
177
|
+
reference: `cli-test-${timestamp}`,
|
|
178
|
+
customer: testCustomer,
|
|
179
|
+
urls: testUrls,
|
|
180
|
+
});
|
|
181
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
182
|
+
},
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
name: 'adyen',
|
|
186
|
+
envRequired: ['ADYEN_API_KEY', 'ADYEN_MERCHANT_ACCOUNT'],
|
|
187
|
+
run: async () => {
|
|
188
|
+
const pay = new index_1.PayBridge({
|
|
189
|
+
provider: 'adyen',
|
|
190
|
+
credentials: {
|
|
191
|
+
apiKey: process.env.ADYEN_API_KEY,
|
|
192
|
+
merchantAccount: process.env.ADYEN_MERCHANT_ACCOUNT,
|
|
193
|
+
},
|
|
194
|
+
sandbox: true,
|
|
195
|
+
});
|
|
196
|
+
const payment = await pay.createPayment({
|
|
197
|
+
amount: 1.0,
|
|
198
|
+
currency: 'EUR',
|
|
199
|
+
reference: `cli-test-${timestamp}`,
|
|
200
|
+
customer: testCustomer,
|
|
201
|
+
urls: testUrls,
|
|
202
|
+
});
|
|
203
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
name: 'mercadopago',
|
|
208
|
+
envRequired: ['MERCADOPAGO_ACCESS_TOKEN'],
|
|
209
|
+
run: async () => {
|
|
210
|
+
const pay = new index_1.PayBridge({
|
|
211
|
+
provider: 'mercadopago',
|
|
212
|
+
credentials: { apiKey: process.env.MERCADOPAGO_ACCESS_TOKEN },
|
|
213
|
+
sandbox: true,
|
|
214
|
+
});
|
|
215
|
+
const payment = await pay.createPayment({
|
|
216
|
+
amount: 1.0,
|
|
217
|
+
currency: 'BRL',
|
|
218
|
+
reference: `cli-test-${timestamp}`,
|
|
219
|
+
customer: testCustomer,
|
|
220
|
+
urls: testUrls,
|
|
221
|
+
});
|
|
222
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
223
|
+
},
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
name: 'razorpay',
|
|
227
|
+
envRequired: ['RAZORPAY_KEY_ID', 'RAZORPAY_KEY_SECRET'],
|
|
228
|
+
run: async () => {
|
|
229
|
+
const pay = new index_1.PayBridge({
|
|
230
|
+
provider: 'razorpay',
|
|
231
|
+
credentials: {
|
|
232
|
+
apiKey: process.env.RAZORPAY_KEY_ID,
|
|
233
|
+
secretKey: process.env.RAZORPAY_KEY_SECRET,
|
|
234
|
+
},
|
|
235
|
+
sandbox: true,
|
|
236
|
+
});
|
|
237
|
+
const payment = await pay.createPayment({
|
|
238
|
+
amount: 1.0,
|
|
239
|
+
currency: 'INR',
|
|
240
|
+
reference: `cli-test-${timestamp}`,
|
|
241
|
+
customer: testCustomer,
|
|
242
|
+
urls: testUrls,
|
|
243
|
+
});
|
|
244
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
name: 'mollie',
|
|
249
|
+
envRequired: ['MOLLIE_API_KEY'],
|
|
250
|
+
run: async () => {
|
|
251
|
+
const pay = new index_1.PayBridge({
|
|
252
|
+
provider: 'mollie',
|
|
253
|
+
credentials: { apiKey: process.env.MOLLIE_API_KEY },
|
|
254
|
+
sandbox: true,
|
|
255
|
+
});
|
|
256
|
+
const payment = await pay.createPayment({
|
|
257
|
+
amount: 1.0,
|
|
258
|
+
currency: 'EUR',
|
|
259
|
+
reference: `cli-test-${timestamp}`,
|
|
260
|
+
customer: testCustomer,
|
|
261
|
+
urls: testUrls,
|
|
262
|
+
});
|
|
263
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
264
|
+
},
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
name: 'square',
|
|
268
|
+
envRequired: ['SQUARE_ACCESS_TOKEN', 'SQUARE_LOCATION_ID'],
|
|
269
|
+
run: async () => {
|
|
270
|
+
const pay = new index_1.PayBridge({
|
|
271
|
+
provider: 'square',
|
|
272
|
+
credentials: {
|
|
273
|
+
apiKey: process.env.SQUARE_ACCESS_TOKEN,
|
|
274
|
+
locationId: process.env.SQUARE_LOCATION_ID,
|
|
275
|
+
},
|
|
276
|
+
sandbox: true,
|
|
277
|
+
});
|
|
278
|
+
const payment = await pay.createPayment({
|
|
279
|
+
amount: 1.0,
|
|
280
|
+
currency: 'USD',
|
|
281
|
+
reference: `cli-test-${timestamp}`,
|
|
282
|
+
customer: testCustomer,
|
|
283
|
+
urls: testUrls,
|
|
284
|
+
});
|
|
285
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
286
|
+
},
|
|
287
|
+
},
|
|
288
|
+
{
|
|
289
|
+
name: 'pesapal',
|
|
290
|
+
envRequired: ['PESAPAL_CONSUMER_KEY', 'PESAPAL_CONSUMER_SECRET'],
|
|
291
|
+
run: async () => {
|
|
292
|
+
const pay = new index_1.PayBridge({
|
|
293
|
+
provider: 'pesapal',
|
|
294
|
+
credentials: {
|
|
295
|
+
apiKey: process.env.PESAPAL_CONSUMER_KEY,
|
|
296
|
+
secretKey: process.env.PESAPAL_CONSUMER_SECRET,
|
|
297
|
+
},
|
|
298
|
+
sandbox: true,
|
|
299
|
+
});
|
|
300
|
+
const payment = await pay.createPayment({
|
|
301
|
+
amount: 1.0,
|
|
302
|
+
currency: 'KES',
|
|
303
|
+
reference: `cli-test-${timestamp}`,
|
|
304
|
+
customer: testCustomer,
|
|
305
|
+
urls: testUrls,
|
|
306
|
+
});
|
|
307
|
+
return { id: payment.id, checkoutUrl: payment.checkoutUrl, status: payment.status };
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: 'moonpay',
|
|
312
|
+
envRequired: ['MOONPAY_API_KEY', 'MOONPAY_SECRET_KEY'],
|
|
313
|
+
run: async () => {
|
|
314
|
+
const ramp = new crypto_1.CryptoRamp({
|
|
315
|
+
provider: 'moonpay',
|
|
316
|
+
credentials: {
|
|
317
|
+
apiKey: process.env.MOONPAY_API_KEY,
|
|
318
|
+
secretKey: process.env.MOONPAY_SECRET_KEY,
|
|
319
|
+
},
|
|
320
|
+
sandbox: true,
|
|
321
|
+
});
|
|
322
|
+
const result = await ramp.createOnRamp({
|
|
323
|
+
fiatAmount: 1.0,
|
|
324
|
+
fiatCurrency: 'USD',
|
|
325
|
+
asset: 'BTC',
|
|
326
|
+
network: 'BTC',
|
|
327
|
+
destinationWallet: '1A1zP1eP5QGefi2DMPTfTL5SLmv7DivfNa',
|
|
328
|
+
customer: testCustomer,
|
|
329
|
+
urls: testUrls,
|
|
330
|
+
reference: `cli-test-${timestamp}`,
|
|
331
|
+
});
|
|
332
|
+
return { id: result.id, checkoutUrl: result.checkoutUrl, status: result.status };
|
|
333
|
+
},
|
|
334
|
+
},
|
|
335
|
+
];
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const colors: {
|
|
2
|
+
reset: string;
|
|
3
|
+
bright: string;
|
|
4
|
+
dim: string;
|
|
5
|
+
green: string;
|
|
6
|
+
yellow: string;
|
|
7
|
+
red: string;
|
|
8
|
+
cyan: string;
|
|
9
|
+
};
|
|
10
|
+
export declare function colorize(text: string, color: keyof typeof colors): string;
|
|
11
|
+
export declare function padEnd(str: string, length: number): string;
|
|
12
|
+
export declare function formatTable(rows: string[][], padding?: number): string;
|
|
13
|
+
export declare function readStdin(): Promise<string>;
|
|
14
|
+
export declare function parseHeaders(headerFlags: string[]): Record<string, string>;
|
|
15
|
+
export declare function printHelp(toStderr?: boolean): void;
|
|
16
|
+
export declare function printVersion(): void;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.colors = void 0;
|
|
4
|
+
exports.colorize = colorize;
|
|
5
|
+
exports.padEnd = padEnd;
|
|
6
|
+
exports.formatTable = formatTable;
|
|
7
|
+
exports.readStdin = readStdin;
|
|
8
|
+
exports.parseHeaders = parseHeaders;
|
|
9
|
+
exports.printHelp = printHelp;
|
|
10
|
+
exports.printVersion = printVersion;
|
|
11
|
+
exports.colors = {
|
|
12
|
+
reset: '\x1b[0m',
|
|
13
|
+
bright: '\x1b[1m',
|
|
14
|
+
dim: '\x1b[2m',
|
|
15
|
+
green: '\x1b[32m',
|
|
16
|
+
yellow: '\x1b[33m',
|
|
17
|
+
red: '\x1b[31m',
|
|
18
|
+
cyan: '\x1b[36m',
|
|
19
|
+
};
|
|
20
|
+
function colorize(text, color) {
|
|
21
|
+
return `${exports.colors[color]}${text}${exports.colors.reset}`;
|
|
22
|
+
}
|
|
23
|
+
function padEnd(str, length) {
|
|
24
|
+
while (str.length < length) {
|
|
25
|
+
str += ' ';
|
|
26
|
+
}
|
|
27
|
+
return str;
|
|
28
|
+
}
|
|
29
|
+
function formatTable(rows, padding = 2) {
|
|
30
|
+
if (rows.length === 0)
|
|
31
|
+
return '';
|
|
32
|
+
const colWidths = [];
|
|
33
|
+
for (const row of rows) {
|
|
34
|
+
row.forEach((cell, i) => {
|
|
35
|
+
colWidths[i] = Math.max(colWidths[i] || 0, cell.length);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
return rows
|
|
39
|
+
.map((row) => row.map((cell, i) => padEnd(cell, colWidths[i])).join(' '.repeat(padding)))
|
|
40
|
+
.join('\n');
|
|
41
|
+
}
|
|
42
|
+
async function readStdin() {
|
|
43
|
+
const chunks = [];
|
|
44
|
+
for await (const chunk of process.stdin) {
|
|
45
|
+
chunks.push(chunk);
|
|
46
|
+
}
|
|
47
|
+
return Buffer.concat(chunks).toString('utf8');
|
|
48
|
+
}
|
|
49
|
+
function parseHeaders(headerFlags) {
|
|
50
|
+
const headers = {};
|
|
51
|
+
for (const flag of headerFlags) {
|
|
52
|
+
const [key, ...valueParts] = flag.split('=');
|
|
53
|
+
if (key && valueParts.length > 0) {
|
|
54
|
+
headers[key.trim()] = valueParts.join('=').trim();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return headers;
|
|
58
|
+
}
|
|
59
|
+
function printHelp(toStderr = false) {
|
|
60
|
+
const output = `
|
|
61
|
+
paybridge — unified payment SDK CLI
|
|
62
|
+
|
|
63
|
+
USAGE
|
|
64
|
+
paybridge <command> [options]
|
|
65
|
+
|
|
66
|
+
COMMANDS
|
|
67
|
+
test <provider> Run sandbox createPayment validation for a provider
|
|
68
|
+
test --all Run validation for every provider with env vars set
|
|
69
|
+
providers [--json] List all providers with capabilities (table or JSON)
|
|
70
|
+
webhook verify <p> Verify webhook signature (raw body from stdin)
|
|
71
|
+
webhook parse <p> Parse webhook event (raw body from stdin)
|
|
72
|
+
quote <p> [opts] Get a crypto on/off-ramp quote
|
|
73
|
+
help, -h, --help Print this help
|
|
74
|
+
version, -v Print version
|
|
75
|
+
|
|
76
|
+
PROVIDER ENV VARS
|
|
77
|
+
See SETUP.md or run 'paybridge test --all' for the full list.
|
|
78
|
+
|
|
79
|
+
EXAMPLES
|
|
80
|
+
paybridge providers
|
|
81
|
+
paybridge test stripe
|
|
82
|
+
STRIPE_API_KEY=sk_test_... paybridge test stripe
|
|
83
|
+
echo '{"id":"evt_x"}' | paybridge webhook parse paystack \\
|
|
84
|
+
--header x-paystack-signature=abc
|
|
85
|
+
|
|
86
|
+
Docs: https://github.com/kobie3717/paybridge
|
|
87
|
+
`.trim();
|
|
88
|
+
if (toStderr) {
|
|
89
|
+
console.error(output);
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(output);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
function printVersion() {
|
|
96
|
+
const pkg = require('../../package.json');
|
|
97
|
+
console.log(`paybridge v${pkg.version}`);
|
|
98
|
+
}
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paybridge",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "One API for fiat + crypto payments. Multi-provider routing, automatic failover, MoonPay on/off-ramp. SA-first, global-ready.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"paybridge": "dist/cli/index.js"
|
|
9
|
+
},
|
|
7
10
|
"scripts": {
|
|
8
11
|
"build": "tsc",
|
|
12
|
+
"build:cli": "npm run build && node scripts/post-build.js",
|
|
9
13
|
"clean": "rm -rf dist",
|
|
10
|
-
"
|
|
11
|
-
"
|
|
14
|
+
"cli": "node dist/cli/index.js",
|
|
15
|
+
"prepack": "npm run clean && npm run build:cli",
|
|
16
|
+
"prepublishOnly": "npm run clean && npm run build:cli",
|
|
12
17
|
"test": "tsc && tsc --project tsconfig.test.json && node --test 'dist-test/**/*.test.js'",
|
|
13
18
|
"test:e2e:moonpay": "tsx tests/e2e/moonpay-sandbox.ts",
|
|
14
19
|
"test:e2e:yellowcard": "tsx tests/e2e/yellowcard-sandbox.ts",
|
|
@@ -39,7 +44,9 @@
|
|
|
39
44
|
"pesapal",
|
|
40
45
|
"transak",
|
|
41
46
|
"ramp",
|
|
42
|
-
"east-africa"
|
|
47
|
+
"east-africa",
|
|
48
|
+
"cli",
|
|
49
|
+
"command-line"
|
|
43
50
|
],
|
|
44
51
|
"author": "Kobie Wentzel",
|
|
45
52
|
"license": "MIT",
|