@orcarail/node 1.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/LICENSE +21 -0
- package/README.md +235 -0
- package/dist/index.cjs +398 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +519 -0
- package/dist/index.d.ts +521 -0
- package/dist/index.js +389 -0
- package/dist/index.js.map +1 -0
- package/package.json +82 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2024 OrcaRail
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
# OrcaRail Node.js SDK
|
|
2
|
+
|
|
3
|
+
[](https://badge.fury.io/js/%40orcarail%2Fnode)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://github.com/orcarail/orcarail-node/actions)
|
|
6
|
+
|
|
7
|
+
Official Node.js SDK for [OrcaRail](https://orcarail.com) - Accept crypto payments with Payment Intents.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- ✅ **Payment Intents** - Create, retrieve, confirm, and update payment intents
|
|
12
|
+
- ✅ **Webhook Verification** - Secure webhook signature verification
|
|
13
|
+
- ✅ **TypeScript Support** - Full TypeScript definitions included
|
|
14
|
+
- ✅ **Zero Dependencies** - Uses native Node.js APIs (Node 18+)
|
|
15
|
+
- ✅ **Dual Package** - Supports both ESM and CommonJS
|
|
16
|
+
|
|
17
|
+
## Installation
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npm install @orcarail/node
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
or
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
yarn add @orcarail/node
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
or
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pnpm add @orcarail/node
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Quick Start
|
|
36
|
+
|
|
37
|
+
```typescript
|
|
38
|
+
import OrcaRail from '@orcarail/node';
|
|
39
|
+
|
|
40
|
+
const orcarail = new OrcaRail('ak_live_xxx', 'sk_live_xxx');
|
|
41
|
+
|
|
42
|
+
// Create a payment intent
|
|
43
|
+
const intent = await orcarail.paymentIntents.create({
|
|
44
|
+
amount: '100.00',
|
|
45
|
+
currency: 'usd',
|
|
46
|
+
payment_method_types: ['crypto'],
|
|
47
|
+
tokenId: 1,
|
|
48
|
+
networkId: 1,
|
|
49
|
+
return_url: 'https://merchant.example.com/return',
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
console.log('Payment Intent ID:', intent.id);
|
|
53
|
+
console.log('Client Secret:', intent.client_secret);
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## API Reference
|
|
57
|
+
|
|
58
|
+
### Payment Intents
|
|
59
|
+
|
|
60
|
+
#### Create a Payment Intent
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
const intent = await orcarail.paymentIntents.create({
|
|
64
|
+
amount: '100.00',
|
|
65
|
+
currency: 'usd',
|
|
66
|
+
payment_method_types: ['crypto'],
|
|
67
|
+
tokenId: 1,
|
|
68
|
+
networkId: 1,
|
|
69
|
+
return_url: 'https://merchant.example.com/return',
|
|
70
|
+
cancel_url: 'https://merchant.example.com/cancel', // optional
|
|
71
|
+
description: 'Payment for services', // optional
|
|
72
|
+
metadata: { order_id: '12345' }, // optional
|
|
73
|
+
expires_at: '2024-12-31T23:59:59Z', // optional
|
|
74
|
+
});
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
#### Retrieve a Payment Intent
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
const intent = await orcarail.paymentIntents.retrieve(
|
|
81
|
+
'pi_1234567890',
|
|
82
|
+
'pi_1234567890_secret_abc123'
|
|
83
|
+
);
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### Confirm a Payment Intent
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
const intent = await orcarail.paymentIntents.confirm('pi_1234567890', {
|
|
90
|
+
client_secret: 'pi_1234567890_secret_abc123',
|
|
91
|
+
return_url: 'https://merchant.example.com/return', // optional
|
|
92
|
+
});
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### Update a Payment Intent
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
const intent = await orcarail.paymentIntents.update('pi_1234567890', {
|
|
99
|
+
amount: '200.00',
|
|
100
|
+
description: 'Updated description',
|
|
101
|
+
metadata: { order_id: '67890' },
|
|
102
|
+
});
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### Webhooks
|
|
106
|
+
|
|
107
|
+
#### Verify Webhook Signature
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
import express from 'express';
|
|
111
|
+
|
|
112
|
+
const app = express();
|
|
113
|
+
|
|
114
|
+
app.post('/webhooks/orcarail', express.raw({ type: 'application/json' }), (req, res) => {
|
|
115
|
+
const signature = req.headers['x-webhook-signature'] as string;
|
|
116
|
+
const webhookSecret = process.env.ORCARAIL_WEBHOOK_SECRET!;
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
const event = orcarail.webhooks.constructEvent(
|
|
120
|
+
req.body,
|
|
121
|
+
signature,
|
|
122
|
+
webhookSecret
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
// Handle the event
|
|
126
|
+
switch (event.type) {
|
|
127
|
+
case 'payment_intent.succeeded':
|
|
128
|
+
console.log('Payment succeeded:', event.data.object.id);
|
|
129
|
+
// Fulfill order, send confirmation email, etc.
|
|
130
|
+
break;
|
|
131
|
+
case 'payment_intent.processing':
|
|
132
|
+
console.log('Payment processing:', event.data.object.id);
|
|
133
|
+
break;
|
|
134
|
+
case 'payment_intent.canceled':
|
|
135
|
+
console.log('Payment canceled:', event.data.object.id);
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
res.json({ received: true });
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.error('Webhook signature verification failed:', error);
|
|
142
|
+
res.status(400).json({ error: 'Invalid signature' });
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
#### Verify Signature (Boolean)
|
|
148
|
+
|
|
149
|
+
```typescript
|
|
150
|
+
const isValid = orcarail.webhooks.verifySignature(
|
|
151
|
+
rawBody,
|
|
152
|
+
signature,
|
|
153
|
+
webhookSecret
|
|
154
|
+
);
|
|
155
|
+
|
|
156
|
+
if (isValid) {
|
|
157
|
+
// Process webhook
|
|
158
|
+
} else {
|
|
159
|
+
// Reject webhook
|
|
160
|
+
}
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
## Configuration
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
const orcarail = new OrcaRail('ak_live_xxx', 'sk_live_xxx', {
|
|
167
|
+
baseUrl: 'https://api.orcarail.com/api/v1', // optional
|
|
168
|
+
timeout: 30000, // optional, default: 30000ms
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Error Handling
|
|
173
|
+
|
|
174
|
+
The SDK throws typed errors that you can catch:
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import {
|
|
178
|
+
OrcaRailError,
|
|
179
|
+
OrcaRailAPIError,
|
|
180
|
+
OrcaRailAuthenticationError,
|
|
181
|
+
OrcaRailSignatureVerificationError,
|
|
182
|
+
} from '@orcarail/node';
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const intent = await orcarail.paymentIntents.create({...});
|
|
186
|
+
} catch (error) {
|
|
187
|
+
if (error instanceof OrcaRailAuthenticationError) {
|
|
188
|
+
console.error('Invalid API credentials');
|
|
189
|
+
} else if (error instanceof OrcaRailAPIError) {
|
|
190
|
+
console.error('API error:', error.statusCode, error.message);
|
|
191
|
+
} else if (error instanceof OrcaRailError) {
|
|
192
|
+
console.error('SDK error:', error.message);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
## TypeScript
|
|
198
|
+
|
|
199
|
+
The SDK is written in TypeScript and includes full type definitions:
|
|
200
|
+
|
|
201
|
+
```typescript
|
|
202
|
+
import type {
|
|
203
|
+
PaymentIntent,
|
|
204
|
+
PaymentIntentCreateParams,
|
|
205
|
+
WebhookEvent,
|
|
206
|
+
WebhookEventType,
|
|
207
|
+
} from '@orcarail/node';
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
## Requirements
|
|
211
|
+
|
|
212
|
+
- Node.js 18.0.0 or higher
|
|
213
|
+
- An OrcaRail account with API keys
|
|
214
|
+
|
|
215
|
+
## Examples
|
|
216
|
+
|
|
217
|
+
See the [examples](./examples/) directory for complete examples:
|
|
218
|
+
|
|
219
|
+
- [Create Payment Intent](./examples/create-payment.ts)
|
|
220
|
+
- [Express Webhook Handler](./examples/webhook-express.ts)
|
|
221
|
+
- [Next.js API Route](./examples/webhook-nextjs.ts)
|
|
222
|
+
|
|
223
|
+
## Contributing
|
|
224
|
+
|
|
225
|
+
Contributions are welcome! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for details.
|
|
226
|
+
|
|
227
|
+
## License
|
|
228
|
+
|
|
229
|
+
MIT License - see [LICENSE](./LICENSE) for details.
|
|
230
|
+
|
|
231
|
+
## Support
|
|
232
|
+
|
|
233
|
+
- [Documentation](https://docs.orcarail.com)
|
|
234
|
+
- [GitHub Issues](https://github.com/orcarail/orcarail-node/issues)
|
|
235
|
+
- [Email Support](mailto:support@orcarail.com)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var crypto = require('crypto');
|
|
6
|
+
|
|
7
|
+
// src/errors.ts
|
|
8
|
+
var OrcaRailError = class extends Error {
|
|
9
|
+
constructor(message) {
|
|
10
|
+
super(message);
|
|
11
|
+
this.name = "OrcaRailError";
|
|
12
|
+
Error.captureStackTrace(this, this.constructor);
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
var OrcaRailAPIError = class extends OrcaRailError {
|
|
16
|
+
/**
|
|
17
|
+
* HTTP status code
|
|
18
|
+
*/
|
|
19
|
+
statusCode;
|
|
20
|
+
/**
|
|
21
|
+
* Error type from the API response
|
|
22
|
+
*/
|
|
23
|
+
type;
|
|
24
|
+
/**
|
|
25
|
+
* Additional error details from the API
|
|
26
|
+
*/
|
|
27
|
+
details;
|
|
28
|
+
constructor(message, statusCode, type, details) {
|
|
29
|
+
super(message);
|
|
30
|
+
this.name = "OrcaRailAPIError";
|
|
31
|
+
this.statusCode = statusCode;
|
|
32
|
+
this.type = type;
|
|
33
|
+
this.details = details;
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
var OrcaRailAuthenticationError = class extends OrcaRailAPIError {
|
|
37
|
+
constructor(message = "Authentication failed. Please check your API key and secret.") {
|
|
38
|
+
super(message, 401, "authentication_error");
|
|
39
|
+
this.name = "OrcaRailAuthenticationError";
|
|
40
|
+
}
|
|
41
|
+
};
|
|
42
|
+
var OrcaRailSignatureVerificationError = class extends OrcaRailError {
|
|
43
|
+
/**
|
|
44
|
+
* The signature that was provided
|
|
45
|
+
*/
|
|
46
|
+
signature;
|
|
47
|
+
constructor(message = "Webhook signature verification failed", signature) {
|
|
48
|
+
super(message);
|
|
49
|
+
this.name = "OrcaRailSignatureVerificationError";
|
|
50
|
+
this.signature = signature || "";
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// src/client.ts
|
|
55
|
+
var DEFAULT_BASE_URL = "https://api.orcarail.com/api/v1";
|
|
56
|
+
var DEFAULT_TIMEOUT = 3e4;
|
|
57
|
+
var SDK_VERSION = "1.0.0";
|
|
58
|
+
var HttpClient = class {
|
|
59
|
+
apiKey;
|
|
60
|
+
apiSecret;
|
|
61
|
+
baseUrl;
|
|
62
|
+
timeout;
|
|
63
|
+
constructor(apiKey, apiSecret, config) {
|
|
64
|
+
if (!apiKey || !apiSecret) {
|
|
65
|
+
throw new OrcaRailError("API key and secret are required");
|
|
66
|
+
}
|
|
67
|
+
this.apiKey = apiKey;
|
|
68
|
+
this.apiSecret = apiSecret;
|
|
69
|
+
this.baseUrl = config?.baseUrl || DEFAULT_BASE_URL;
|
|
70
|
+
this.timeout = config?.timeout || DEFAULT_TIMEOUT;
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* Create Basic Auth header from API key and secret
|
|
74
|
+
*/
|
|
75
|
+
getAuthHeader() {
|
|
76
|
+
const credentials = Buffer.from(`${this.apiKey}:${this.apiSecret}`).toString(
|
|
77
|
+
"base64"
|
|
78
|
+
);
|
|
79
|
+
return `Basic ${credentials}`;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Build full URL from path
|
|
83
|
+
*/
|
|
84
|
+
buildUrl(path) {
|
|
85
|
+
const cleanPath = path.startsWith("/") ? path.slice(1) : path;
|
|
86
|
+
return `${this.baseUrl}/${cleanPath}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Make an HTTP request with timeout and error handling
|
|
90
|
+
*/
|
|
91
|
+
async request(method, path, body, requireAuth = true) {
|
|
92
|
+
const url = this.buildUrl(path);
|
|
93
|
+
const headers = {
|
|
94
|
+
"Content-Type": "application/json",
|
|
95
|
+
"User-Agent": `orcarail-node/${SDK_VERSION}`
|
|
96
|
+
};
|
|
97
|
+
if (requireAuth) {
|
|
98
|
+
headers["Authorization"] = this.getAuthHeader();
|
|
99
|
+
}
|
|
100
|
+
const options = {
|
|
101
|
+
method,
|
|
102
|
+
headers
|
|
103
|
+
};
|
|
104
|
+
if (body) {
|
|
105
|
+
options.body = JSON.stringify(body);
|
|
106
|
+
}
|
|
107
|
+
const controller = new AbortController();
|
|
108
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
109
|
+
options.signal = controller.signal;
|
|
110
|
+
try {
|
|
111
|
+
const response = await fetch(url, options);
|
|
112
|
+
clearTimeout(timeoutId);
|
|
113
|
+
let responseData;
|
|
114
|
+
const contentType = response.headers.get("content-type");
|
|
115
|
+
if (contentType && contentType.includes("application/json")) {
|
|
116
|
+
responseData = await response.json();
|
|
117
|
+
} else {
|
|
118
|
+
responseData = await response.text();
|
|
119
|
+
}
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
const errorMessage = typeof responseData === "object" && responseData !== null && "message" in responseData ? String(responseData.message) : `API request failed with status ${response.status}`;
|
|
122
|
+
const errorType = typeof responseData === "object" && responseData !== null && "error" in responseData ? String(responseData.error) : void 0;
|
|
123
|
+
if (response.status === 401) {
|
|
124
|
+
throw new OrcaRailAuthenticationError(errorMessage);
|
|
125
|
+
}
|
|
126
|
+
throw new OrcaRailAPIError(
|
|
127
|
+
errorMessage,
|
|
128
|
+
response.status,
|
|
129
|
+
errorType,
|
|
130
|
+
responseData
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return responseData;
|
|
134
|
+
} catch (error) {
|
|
135
|
+
clearTimeout(timeoutId);
|
|
136
|
+
if (error instanceof OrcaRailError) {
|
|
137
|
+
throw error;
|
|
138
|
+
}
|
|
139
|
+
if (error instanceof Error) {
|
|
140
|
+
if (error.name === "AbortError") {
|
|
141
|
+
throw new OrcaRailError(
|
|
142
|
+
`Request timeout after ${this.timeout}ms`
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
throw new OrcaRailError(`Request failed: ${error.message}`);
|
|
146
|
+
}
|
|
147
|
+
throw new OrcaRailError("An unknown error occurred");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* GET request
|
|
152
|
+
*/
|
|
153
|
+
async get(path, requireAuth = true) {
|
|
154
|
+
return this.request("GET", path, void 0, requireAuth);
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* POST request
|
|
158
|
+
*/
|
|
159
|
+
async post(path, body, requireAuth = true) {
|
|
160
|
+
return this.request("POST", path, body, requireAuth);
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* PATCH request
|
|
164
|
+
*/
|
|
165
|
+
async patch(path, body, requireAuth = true) {
|
|
166
|
+
return this.request("PATCH", path, body, requireAuth);
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* PUT request
|
|
170
|
+
*/
|
|
171
|
+
async put(path, body, requireAuth = true) {
|
|
172
|
+
return this.request("PUT", path, body, requireAuth);
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* DELETE request
|
|
176
|
+
*/
|
|
177
|
+
async delete(path, requireAuth = true) {
|
|
178
|
+
return this.request("DELETE", path, void 0, requireAuth);
|
|
179
|
+
}
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
// src/resources/checkout.ts
|
|
183
|
+
var Checkout = class {
|
|
184
|
+
client;
|
|
185
|
+
constructor(client) {
|
|
186
|
+
this.client = client;
|
|
187
|
+
}
|
|
188
|
+
/**
|
|
189
|
+
* Get checkout details by slug
|
|
190
|
+
*
|
|
191
|
+
* @param slug - Checkout slug from the payment link URL
|
|
192
|
+
* @returns Checkout details including payment intent
|
|
193
|
+
*/
|
|
194
|
+
async get(slug) {
|
|
195
|
+
const path = `checkout/${encodeURIComponent(slug)}`;
|
|
196
|
+
return this.client.get(path, true);
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* Cancel payment intent by checkout slug
|
|
200
|
+
*
|
|
201
|
+
* @param slug - Checkout slug from the payment link URL
|
|
202
|
+
* @returns The canceled payment intent and optional cancel_url for redirect
|
|
203
|
+
*/
|
|
204
|
+
async cancel(slug) {
|
|
205
|
+
const path = `checkout/${encodeURIComponent(slug)}/cancel`;
|
|
206
|
+
return this.client.post(path, {}, true);
|
|
207
|
+
}
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// src/resources/payment-intents.ts
|
|
211
|
+
var PaymentIntents = class {
|
|
212
|
+
client;
|
|
213
|
+
constructor(client) {
|
|
214
|
+
this.client = client;
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Create a new Payment Intent
|
|
218
|
+
*
|
|
219
|
+
* @param params - Payment Intent creation parameters
|
|
220
|
+
* @returns The created Payment Intent
|
|
221
|
+
*/
|
|
222
|
+
async create(params) {
|
|
223
|
+
const requestBody = {
|
|
224
|
+
...params,
|
|
225
|
+
payment_method_types: params.payment_method_types && params.payment_method_types.length > 0 ? params.payment_method_types : ["crypto"]
|
|
226
|
+
};
|
|
227
|
+
return this.client.post(
|
|
228
|
+
"payment_intents",
|
|
229
|
+
requestBody,
|
|
230
|
+
true
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Retrieve a Payment Intent by ID
|
|
235
|
+
*
|
|
236
|
+
* @param id - Payment Intent ID (e.g., "pi_1234567890")
|
|
237
|
+
* @returns The Payment Intent
|
|
238
|
+
*/
|
|
239
|
+
async retrieve(id) {
|
|
240
|
+
const cleanId = id.startsWith("pi_") ? id : `pi_${id}`;
|
|
241
|
+
const path = `payment_intents/${cleanId}`;
|
|
242
|
+
return this.client.get(path, true);
|
|
243
|
+
}
|
|
244
|
+
/**
|
|
245
|
+
* Cancel a Payment Intent (API: POST /payment_intents/:id/cancel).
|
|
246
|
+
* Use when the user is redirected to your cancel_url (e.g. https://yourapp.com/cancel?payment_intent=pi_20)
|
|
247
|
+
* and you want to mark the intent as canceled on the backend.
|
|
248
|
+
*
|
|
249
|
+
* @param id - Payment Intent ID (e.g. "pi_20" or "20")
|
|
250
|
+
* @returns The canceled Payment Intent
|
|
251
|
+
*
|
|
252
|
+
* @example
|
|
253
|
+
* // When user lands on https://localhost:3001/cancel?payment_intent=pi_20
|
|
254
|
+
* const intent = await orcarail.paymentIntents.cancel('pi_20');
|
|
255
|
+
*/
|
|
256
|
+
async cancel(id) {
|
|
257
|
+
const cleanId = id.startsWith("pi_") ? id : `pi_${id}`;
|
|
258
|
+
const path = `payment_intents/${cleanId}/cancel`;
|
|
259
|
+
return this.client.post(path, {}, true);
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* Confirm a Payment Intent (API: POST /payment_intents/:id/confirm).
|
|
263
|
+
* Redirects the customer to the hosted checkout page.
|
|
264
|
+
*
|
|
265
|
+
* @param id - Payment Intent ID (e.g. "pi_20" or "20")
|
|
266
|
+
* @param params - Confirmation parameters including client_secret and return_url
|
|
267
|
+
* @returns The confirmed Payment Intent
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* const intent = await orcarail.paymentIntents.confirm('pi_20', {
|
|
271
|
+
* client_secret: intent.client_secret,
|
|
272
|
+
* return_url: 'https://yourapp.com/return',
|
|
273
|
+
* });
|
|
274
|
+
*/
|
|
275
|
+
async confirm(id, params) {
|
|
276
|
+
const cleanId = id.startsWith("pi_") ? id : `pi_${id}`;
|
|
277
|
+
const path = `payment_intents/${cleanId}/confirm`;
|
|
278
|
+
return this.client.post(path, params, false);
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Complete a Payment Intent (API: POST /payment_intents/:id/complete).
|
|
282
|
+
* Sets the intent to processing and fires the payment_intent.processing webhook.
|
|
283
|
+
* Use when the user is redirected to your success/return URL (e.g. https://yourapp.com/success?payment_intent=pi_34).
|
|
284
|
+
*
|
|
285
|
+
* @param id - Payment Intent ID (e.g. "pi_34" or "34")
|
|
286
|
+
* @returns The updated Payment Intent (status processing)
|
|
287
|
+
*
|
|
288
|
+
* @example
|
|
289
|
+
* // When user lands on https://localhost:3001/success?payment_intent=pi_34
|
|
290
|
+
* const intent = await orcarail.paymentIntents.complete('pi_34');
|
|
291
|
+
*/
|
|
292
|
+
async complete(id) {
|
|
293
|
+
const cleanId = id.startsWith("pi_") ? id : `pi_${id}`;
|
|
294
|
+
const path = `payment_intents/${cleanId}/complete`;
|
|
295
|
+
return this.client.post(path, {}, true);
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* Update a Payment Intent
|
|
299
|
+
*
|
|
300
|
+
* @param id - Payment Intent ID
|
|
301
|
+
* @param params - Update parameters (all optional)
|
|
302
|
+
* @returns The updated Payment Intent
|
|
303
|
+
*/
|
|
304
|
+
async update(id, params) {
|
|
305
|
+
const cleanId = id.startsWith("pi_") ? id : `pi_${id}`;
|
|
306
|
+
const path = `payment_intents/${cleanId}`;
|
|
307
|
+
return this.client.patch(path, params, true);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
var Webhooks = class {
|
|
311
|
+
/**
|
|
312
|
+
* Verify webhook signature
|
|
313
|
+
*
|
|
314
|
+
* @param rawBody - Raw request body (string or Buffer)
|
|
315
|
+
* @param signature - Signature from x-webhook-signature header
|
|
316
|
+
* @param secret - Webhook secret (API key's secretHash)
|
|
317
|
+
* @returns True if signature is valid, false otherwise
|
|
318
|
+
*/
|
|
319
|
+
verifySignature(rawBody, signature, secret) {
|
|
320
|
+
if (!signature || !secret) {
|
|
321
|
+
return false;
|
|
322
|
+
}
|
|
323
|
+
const bodyString = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
|
|
324
|
+
const expectedSignature = crypto.createHmac("sha256", secret).update(bodyString).digest("hex");
|
|
325
|
+
const signatureBuffer = Buffer.from(signature, "hex");
|
|
326
|
+
const expectedBuffer = Buffer.from(expectedSignature, "hex");
|
|
327
|
+
if (signatureBuffer.length !== expectedBuffer.length) {
|
|
328
|
+
return false;
|
|
329
|
+
}
|
|
330
|
+
return crypto.timingSafeEqual(signatureBuffer, expectedBuffer);
|
|
331
|
+
}
|
|
332
|
+
/**
|
|
333
|
+
* Construct and verify a webhook event from raw body and signature
|
|
334
|
+
*
|
|
335
|
+
* @param rawBody - Raw request body (string or Buffer)
|
|
336
|
+
* @param signature - Signature from x-webhook-signature header
|
|
337
|
+
* @param secret - Webhook secret (API key's secretHash)
|
|
338
|
+
* @returns Parsed webhook event
|
|
339
|
+
* @throws OrcaRailSignatureVerificationError if signature is invalid
|
|
340
|
+
*/
|
|
341
|
+
constructEvent(rawBody, signature, secret) {
|
|
342
|
+
if (!this.verifySignature(rawBody, signature, secret)) {
|
|
343
|
+
throw new OrcaRailSignatureVerificationError(
|
|
344
|
+
"Webhook signature verification failed. The signature does not match the expected signature.",
|
|
345
|
+
signature
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
const bodyString = typeof rawBody === "string" ? rawBody : rawBody.toString("utf8");
|
|
349
|
+
try {
|
|
350
|
+
const event = JSON.parse(bodyString);
|
|
351
|
+
return event;
|
|
352
|
+
} catch (error) {
|
|
353
|
+
throw new OrcaRailSignatureVerificationError(
|
|
354
|
+
`Failed to parse webhook body: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
};
|
|
359
|
+
|
|
360
|
+
// src/index.ts
|
|
361
|
+
var OrcaRail = class {
|
|
362
|
+
/**
|
|
363
|
+
* Payment Intents resource
|
|
364
|
+
*/
|
|
365
|
+
paymentIntents;
|
|
366
|
+
/**
|
|
367
|
+
* Checkout resource (slug-based get/cancel)
|
|
368
|
+
*/
|
|
369
|
+
checkout;
|
|
370
|
+
/**
|
|
371
|
+
* Webhooks utilities
|
|
372
|
+
*/
|
|
373
|
+
webhooks;
|
|
374
|
+
client;
|
|
375
|
+
/**
|
|
376
|
+
* Create a new OrcaRail client instance
|
|
377
|
+
*
|
|
378
|
+
* @param apiKey - Your OrcaRail API key (e.g., "ak_live_xxx")
|
|
379
|
+
* @param apiSecret - Your OrcaRail API secret (e.g., "sk_live_xxx")
|
|
380
|
+
* @param config - Optional configuration
|
|
381
|
+
*/
|
|
382
|
+
constructor(apiKey, apiSecret, config) {
|
|
383
|
+
this.client = new HttpClient(apiKey, apiSecret, config);
|
|
384
|
+
this.paymentIntents = new PaymentIntents(this.client);
|
|
385
|
+
this.checkout = new Checkout(this.client);
|
|
386
|
+
this.webhooks = new Webhooks();
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
var index_default = OrcaRail;
|
|
390
|
+
|
|
391
|
+
exports.OrcaRail = OrcaRail;
|
|
392
|
+
exports.OrcaRailAPIError = OrcaRailAPIError;
|
|
393
|
+
exports.OrcaRailAuthenticationError = OrcaRailAuthenticationError;
|
|
394
|
+
exports.OrcaRailError = OrcaRailError;
|
|
395
|
+
exports.OrcaRailSignatureVerificationError = OrcaRailSignatureVerificationError;
|
|
396
|
+
exports.default = index_default;
|
|
397
|
+
//# sourceMappingURL=index.cjs.map
|
|
398
|
+
//# sourceMappingURL=index.cjs.map
|