blaaiz-nodejs-sdk 1.1.0 → 1.1.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/README.md +1 -1
- package/index.d.ts +3 -1
- package/package.json +1 -1
- package/src/index.js +1 -1
- package/src/services/CustomerService.js +93 -1
package/README.md
CHANGED
|
@@ -128,7 +128,7 @@ const result = await blaaiz.customers.uploadFileComplete('customer-id', {
|
|
|
128
128
|
file: fileBuffer, // Buffer or Uint8Array
|
|
129
129
|
file_category: 'identity', // identity, proof_of_address, liveness_check
|
|
130
130
|
filename: 'passport.jpg', // Optional
|
|
131
|
-
|
|
131
|
+
content_type: 'image/jpeg' // Optional
|
|
132
132
|
});
|
|
133
133
|
|
|
134
134
|
// Option B: Upload from Base64 string
|
package/index.d.ts
CHANGED
|
@@ -52,6 +52,8 @@ export interface FileUploadOptions {
|
|
|
52
52
|
file: Buffer | Uint8Array | string;
|
|
53
53
|
file_category: 'identity' | 'proof_of_address' | 'liveness_check';
|
|
54
54
|
filename?: string;
|
|
55
|
+
content_type?: string;
|
|
56
|
+
/** @deprecated Use content_type instead */
|
|
55
57
|
contentType?: string;
|
|
56
58
|
}
|
|
57
59
|
|
|
@@ -331,7 +333,7 @@ export declare class Blaaiz {
|
|
|
331
333
|
|
|
332
334
|
testConnection(): Promise<boolean>;
|
|
333
335
|
|
|
334
|
-
|
|
336
|
+
createCompletePayout(payoutConfig: {
|
|
335
337
|
customerData?: CustomerData;
|
|
336
338
|
payoutData: PayoutData;
|
|
337
339
|
}): Promise<{
|
package/package.json
CHANGED
package/src/index.js
CHANGED
|
@@ -84,7 +84,8 @@ class CustomerService {
|
|
|
84
84
|
}
|
|
85
85
|
|
|
86
86
|
const { file, file_category } = fileOptions // eslint-disable-line camelcase
|
|
87
|
-
let { filename
|
|
87
|
+
let { filename } = fileOptions
|
|
88
|
+
let contentType = fileOptions.content_type || fileOptions.contentType // eslint-disable-line camelcase
|
|
88
89
|
|
|
89
90
|
if (!file) {
|
|
90
91
|
throw new Error('File is required')
|
|
@@ -129,6 +130,15 @@ class CustomerService {
|
|
|
129
130
|
if (file.startsWith('data:')) {
|
|
130
131
|
// Handle data URL format: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAABAAEDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAv/xAAUEAEAAAAAAAAAAAAAAAAAAAAA/8QAFQEBAQAAAAAAAAAAAAAAAAAAAAX/xAAUEQEAAAAAAAAAAAAAAAAAAAAA/9oADAMBAAIRAxEAPwCdABmX/9k=
|
|
131
132
|
const base64Data = file.split(',')[1]
|
|
133
|
+
if (!base64Data || !base64Data.trim()) {
|
|
134
|
+
throw new Error('Invalid data URL: no base64 data found after the comma')
|
|
135
|
+
}
|
|
136
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(base64Data.trim())) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
'The base64 portion of the data URL does not appear to be valid base64. ' +
|
|
139
|
+
'Ensure the string after the comma contains only valid base64 characters.'
|
|
140
|
+
)
|
|
141
|
+
}
|
|
132
142
|
fileBuffer = Buffer.from(base64Data, 'base64')
|
|
133
143
|
|
|
134
144
|
// Extract content type from data URL if not provided
|
|
@@ -154,6 +164,15 @@ class CustomerService {
|
|
|
154
164
|
}
|
|
155
165
|
} else {
|
|
156
166
|
// Handle plain base64 string
|
|
167
|
+
if (!file.trim()) {
|
|
168
|
+
throw new Error('The file string is empty')
|
|
169
|
+
}
|
|
170
|
+
if (!/^[A-Za-z0-9+/]*={0,2}$/.test(file.trim())) {
|
|
171
|
+
throw new Error(
|
|
172
|
+
'The file string does not appear to be valid base64. ' +
|
|
173
|
+
'If you meant to pass a file path, Buffer, or URL, use the appropriate format instead.'
|
|
174
|
+
)
|
|
175
|
+
}
|
|
157
176
|
fileBuffer = Buffer.from(file, 'base64')
|
|
158
177
|
}
|
|
159
178
|
} else {
|
|
@@ -161,6 +180,19 @@ class CustomerService {
|
|
|
161
180
|
fileBuffer = Buffer.from(file)
|
|
162
181
|
}
|
|
163
182
|
|
|
183
|
+
// Auto-detect content type if not provided
|
|
184
|
+
if (!contentType) {
|
|
185
|
+
contentType = this._detectContentTypeFromBytes(fileBuffer)
|
|
186
|
+
}
|
|
187
|
+
if (!contentType && filename) {
|
|
188
|
+
contentType = this._getContentTypeFromFilename(filename)
|
|
189
|
+
}
|
|
190
|
+
if (!contentType) {
|
|
191
|
+
throw new Error(
|
|
192
|
+
'Could not determine file content type. Please provide a content_type (e.g., "image/jpeg", "image/png", "application/pdf") in fileOptions.'
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
164
196
|
await this._uploadToS3(presignedUrl, fileBuffer, contentType, filename)
|
|
165
197
|
|
|
166
198
|
// Map file category to the correct field name expected by Laravel API
|
|
@@ -353,6 +385,66 @@ class CustomerService {
|
|
|
353
385
|
})
|
|
354
386
|
}
|
|
355
387
|
|
|
388
|
+
_detectContentTypeFromBytes (buffer) {
|
|
389
|
+
if (!buffer || buffer.length < 4) return null
|
|
390
|
+
|
|
391
|
+
// JPEG: FF D8 FF
|
|
392
|
+
if (buffer[0] === 0xFF && buffer[1] === 0xD8 && buffer[2] === 0xFF) {
|
|
393
|
+
return 'image/jpeg'
|
|
394
|
+
}
|
|
395
|
+
// PNG: 89 50 4E 47
|
|
396
|
+
if (buffer[0] === 0x89 && buffer[1] === 0x50 && buffer[2] === 0x4E && buffer[3] === 0x47) {
|
|
397
|
+
return 'image/png'
|
|
398
|
+
}
|
|
399
|
+
// GIF: 47 49 46 38
|
|
400
|
+
if (buffer[0] === 0x47 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x38) {
|
|
401
|
+
return 'image/gif'
|
|
402
|
+
}
|
|
403
|
+
// PDF: 25 50 44 46 (%PDF)
|
|
404
|
+
if (buffer[0] === 0x25 && buffer[1] === 0x50 && buffer[2] === 0x44 && buffer[3] === 0x46) {
|
|
405
|
+
return 'application/pdf'
|
|
406
|
+
}
|
|
407
|
+
// WEBP: starts with RIFF....WEBP
|
|
408
|
+
if (buffer.length >= 12 &&
|
|
409
|
+
buffer[0] === 0x52 && buffer[1] === 0x49 && buffer[2] === 0x46 && buffer[3] === 0x46 &&
|
|
410
|
+
buffer[8] === 0x57 && buffer[9] === 0x45 && buffer[10] === 0x42 && buffer[11] === 0x50) {
|
|
411
|
+
return 'image/webp'
|
|
412
|
+
}
|
|
413
|
+
// BMP: 42 4D
|
|
414
|
+
if (buffer[0] === 0x42 && buffer[1] === 0x4D) {
|
|
415
|
+
return 'image/bmp'
|
|
416
|
+
}
|
|
417
|
+
// TIFF: 49 49 2A 00 (little-endian) or 4D 4D 00 2A (big-endian)
|
|
418
|
+
if ((buffer[0] === 0x49 && buffer[1] === 0x49 && buffer[2] === 0x2A && buffer[3] === 0x00) ||
|
|
419
|
+
(buffer[0] === 0x4D && buffer[1] === 0x4D && buffer[2] === 0x00 && buffer[3] === 0x2A)) {
|
|
420
|
+
return 'image/tiff'
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
return null
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
_getContentTypeFromFilename (filename) {
|
|
427
|
+
if (!filename) return null
|
|
428
|
+
|
|
429
|
+
const ext = filename.toLowerCase().split('.').pop()
|
|
430
|
+
const extToMime = {
|
|
431
|
+
jpg: 'image/jpeg',
|
|
432
|
+
jpeg: 'image/jpeg',
|
|
433
|
+
png: 'image/png',
|
|
434
|
+
gif: 'image/gif',
|
|
435
|
+
webp: 'image/webp',
|
|
436
|
+
bmp: 'image/bmp',
|
|
437
|
+
tiff: 'image/tiff',
|
|
438
|
+
tif: 'image/tiff',
|
|
439
|
+
pdf: 'application/pdf',
|
|
440
|
+
txt: 'text/plain',
|
|
441
|
+
doc: 'application/msword',
|
|
442
|
+
docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return extToMime[ext] || null
|
|
446
|
+
}
|
|
447
|
+
|
|
356
448
|
_getExtensionFromContentType (contentType) {
|
|
357
449
|
const mimeToExt = {
|
|
358
450
|
'image/jpeg': '.jpg',
|