@ripwords/myinvois-client 0.1.5 → 0.1.7
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/package.json +8 -11
- package/.prettierrc +0 -8
- package/CHANGELOG.md +0 -140
- package/bun.lock +0 -460
- package/myinvois-cert.conf.template +0 -23
- package/scripts/gen-cert.sh +0 -159
- package/src/api/platform/platformLogin.ts +0 -34
- package/src/index.ts +0 -530
- package/src/types/classification-codes.d.ts +0 -115
- package/src/types/country-code.d.ts +0 -790
- package/src/types/currencies.d.ts +0 -383
- package/src/types/documents.d.ts +0 -869
- package/src/types/e-invoice.d.ts +0 -41
- package/src/types/index.d.ts +0 -24
- package/src/types/msic/0X.d.ts +0 -408
- package/src/types/msic/1X.d.ts +0 -210
- package/src/types/msic/2X.d.ts +0 -266
- package/src/types/msic/3X.d.ts +0 -114
- package/src/types/msic/4X.d.ts +0 -520
- package/src/types/msic/5X.d.ts +0 -144
- package/src/types/msic/6X.d.ts +0 -200
- package/src/types/msic/7X.d.ts +0 -132
- package/src/types/msic/8X.d.ts +0 -210
- package/src/types/msic/9X.d.ts +0 -186
- package/src/types/msic-codes.d.ts +0 -31
- package/src/types/payment-modes.d.ts +0 -41
- package/src/types/signatures.d.ts +0 -169
- package/src/types/state-codes.d.ts +0 -59
- package/src/types/tax-types.d.ts +0 -39
- package/src/types/unit/1X.d.ts +0 -16
- package/src/types/unit/2X.d.ts +0 -62
- package/src/types/unit/3X.d.ts +0 -17
- package/src/types/unit/4X.d.ts +0 -44
- package/src/types/unit/5X.d.ts +0 -26
- package/src/types/unit/6X.d.ts +0 -12
- package/src/types/unit/7X.d.ts +0 -12
- package/src/types/unit/8X.d.ts +0 -15
- package/src/types/unit/9X.d.ts +0 -11
- package/src/types/unit/AX.d.ts +0 -202
- package/src/types/unit/BX.d.ts +0 -212
- package/src/types/unit/CX.d.ts +0 -238
- package/src/types/unit/DX.d.ts +0 -212
- package/src/types/unit/EX.d.ts +0 -196
- package/src/types/unit/FX.d.ts +0 -236
- package/src/types/unit/GX.d.ts +0 -254
- package/src/types/unit/HX.d.ts +0 -234
- package/src/types/unit/IX.d.ts +0 -28
- package/src/types/unit/JX.d.ts +0 -190
- package/src/types/unit/KX.d.ts +0 -284
- package/src/types/unit/LX.d.ts +0 -228
- package/src/types/unit/MX.d.ts +0 -288
- package/src/types/unit/NX.d.ts +0 -226
- package/src/types/unit/OX.d.ts +0 -34
- package/src/types/unit/PX.d.ts +0 -224
- package/src/types/unit/QX.d.ts +0 -94
- package/src/types/unit/RX.d.ts +0 -28
- package/src/types/unit/SX.d.ts +0 -56
- package/src/types/unit/TX.d.ts +0 -44
- package/src/types/unit/UX.d.ts +0 -14
- package/src/types/unit/VX.d.ts +0 -13
- package/src/types/unit/WX.d.ts +0 -34
- package/src/types/unit/XX.d.ts +0 -825
- package/src/types/unit/YX.d.ts +0 -17
- package/src/types/unit/ZX.d.ts +0 -19
- package/src/types/unit-types.d.ts +0 -86
- package/src/utils/base64.ts +0 -7
- package/src/utils/certificate.ts +0 -60
- package/src/utils/document.ts +0 -852
- package/src/utils/getBaseUrl.ts +0 -5
- package/src/utils/helpers.ts +0 -552
- package/src/utils/signature-diagnostics.ts +0 -583
- package/src/utils/validation.ts +0 -268
- package/test/MyInvoiClientWithRealData.test.ts +0 -40
- package/test/MyInvoisClient.test.ts +0 -204
- package/test/base64.test.ts +0 -43
- package/test/dynamicInvoiceFeatures.test.ts +0 -451
- package/test/signAndSubmitInvoice.test.ts +0 -452
- package/test/signature-diagnostics.test.ts +0 -130
- package/tsconfig.json +0 -39
- package/tsdown.config.ts +0 -31
- package/vitest.config.ts +0 -8
|
@@ -1,452 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'vitest'
|
|
2
|
-
import { MyInvoisClient } from '../src/index'
|
|
3
|
-
import type { InvoiceV1_1 } from '../src/types'
|
|
4
|
-
import {
|
|
5
|
-
generateCompleteDocument,
|
|
6
|
-
extractCertificateInfo,
|
|
7
|
-
} from '../src/utils/document'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* ⚠️ SECURITY NOTICE: This file uses environment variables for sensitive data.
|
|
11
|
-
* Never hardcode actual TIN, NRIC, certificates, or API credentials in test files.
|
|
12
|
-
* Use .env file for your actual values (already gitignored).
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Creates minimal test invoice data that meets all mandatory requirements
|
|
17
|
-
* Following MyInvois v1.1 specification exactly
|
|
18
|
-
*/
|
|
19
|
-
const createMinimalTestInvoice = (): InvoiceV1_1 => {
|
|
20
|
-
// Use current date/time to avoid validation errors
|
|
21
|
-
const now = new Date()
|
|
22
|
-
const currentDate = now.toISOString().split('T')[0] // YYYY-MM-DD
|
|
23
|
-
const currentTime = now.toISOString().split('T')[1].split('.')[0] + 'Z' // HH:MM:SSZ
|
|
24
|
-
|
|
25
|
-
return {
|
|
26
|
-
// === CORE MANDATORY FIELDS ===
|
|
27
|
-
eInvoiceVersion: '1.1',
|
|
28
|
-
eInvoiceTypeCode: '01', // Standard invoice
|
|
29
|
-
eInvoiceCodeOrNumber: `TEST-INV-${Date.now()}`, // Unique invoice number
|
|
30
|
-
eInvoiceDate: currentDate,
|
|
31
|
-
eInvoiceTime: currentTime,
|
|
32
|
-
invoiceCurrencyCode: 'MYR',
|
|
33
|
-
|
|
34
|
-
// === SUPPLIER (will be updated with certificate TIN) ===
|
|
35
|
-
supplier: {
|
|
36
|
-
name: 'Test Company Sdn Bhd',
|
|
37
|
-
tin: process.env.TIN_VALUE!, // Will be replaced with certificate TIN
|
|
38
|
-
registrationType: 'NRIC',
|
|
39
|
-
registrationNumber: process.env.NRIC_VALUE!,
|
|
40
|
-
contactNumber: '+60123456789',
|
|
41
|
-
address: {
|
|
42
|
-
addressLine0: '123 Test Street',
|
|
43
|
-
cityName: 'Kuala Lumpur',
|
|
44
|
-
state: '14', // Wilayah Persekutuan Kuala Lumpur
|
|
45
|
-
country: 'MYS',
|
|
46
|
-
},
|
|
47
|
-
industryClassificationCode: '41001',
|
|
48
|
-
industryClassificationDescription: 'Test Industry',
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
// === BUYER (using consolidated buyer for testing) ===
|
|
52
|
-
buyer: {
|
|
53
|
-
name: 'CONSOLIDATED E-INVOICE BUYER',
|
|
54
|
-
tin: 'EI00000000010', // Standard consolidated buyer TIN
|
|
55
|
-
registrationType: 'NRIC',
|
|
56
|
-
registrationNumber: process.env.NRIC_VALUE!,
|
|
57
|
-
sstRegistrationNumber: 'NA',
|
|
58
|
-
contactNumber: '+60123456789', // Valid phone number (minimum 8 chars)
|
|
59
|
-
address: {
|
|
60
|
-
addressLine0: 'NA',
|
|
61
|
-
cityName: 'KUALA LUMPUR',
|
|
62
|
-
state: '14',
|
|
63
|
-
country: 'MYS',
|
|
64
|
-
},
|
|
65
|
-
},
|
|
66
|
-
|
|
67
|
-
// === SINGLE LINE ITEM (minimal) ===
|
|
68
|
-
invoiceLineItems: [
|
|
69
|
-
{
|
|
70
|
-
itemClassificationCode: '001', // General goods
|
|
71
|
-
itemDescription: 'Test Product',
|
|
72
|
-
unitPrice: 100.0,
|
|
73
|
-
taxType: '01', // SST
|
|
74
|
-
taxRate: 6.0, // 6% SST
|
|
75
|
-
taxAmount: 6.0, // 6% of 100
|
|
76
|
-
totalTaxableAmountPerLine: 100.0,
|
|
77
|
-
totalAmountPerLine: 106.0, // 100 + 6
|
|
78
|
-
},
|
|
79
|
-
],
|
|
80
|
-
|
|
81
|
-
// === MONETARY TOTALS ===
|
|
82
|
-
legalMonetaryTotal: {
|
|
83
|
-
taxExclusiveAmount: 100.0,
|
|
84
|
-
taxInclusiveAmount: 106.0,
|
|
85
|
-
payableAmount: 106.0,
|
|
86
|
-
},
|
|
87
|
-
|
|
88
|
-
// === TAX TOTAL ===
|
|
89
|
-
taxTotal: {
|
|
90
|
-
taxAmount: 6.0,
|
|
91
|
-
},
|
|
92
|
-
|
|
93
|
-
// === DIGITAL SIGNATURE (placeholder - will be populated by signing process) ===
|
|
94
|
-
issuerDigitalSignature: {
|
|
95
|
-
Id: 'signature',
|
|
96
|
-
'ds:SignedInfo': {
|
|
97
|
-
'ds:CanonicalizationMethod': {
|
|
98
|
-
Algorithm: 'http://www.w3.org/2006/12/xml-c14n11',
|
|
99
|
-
},
|
|
100
|
-
'ds:SignatureMethod': {
|
|
101
|
-
Algorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',
|
|
102
|
-
},
|
|
103
|
-
'ds:Reference': [
|
|
104
|
-
{
|
|
105
|
-
Id: 'id-doc-signed-data',
|
|
106
|
-
URI: '',
|
|
107
|
-
'ds:DigestMethod': {
|
|
108
|
-
Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
|
|
109
|
-
},
|
|
110
|
-
'ds:DigestValue': '',
|
|
111
|
-
},
|
|
112
|
-
{
|
|
113
|
-
Id: 'id-xades-signed-props',
|
|
114
|
-
URI: '#id-xades-signed-props',
|
|
115
|
-
'ds:DigestMethod': {
|
|
116
|
-
Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
|
|
117
|
-
},
|
|
118
|
-
'ds:DigestValue': '',
|
|
119
|
-
},
|
|
120
|
-
],
|
|
121
|
-
},
|
|
122
|
-
'ds:SignatureValue': '',
|
|
123
|
-
'ds:KeyInfo': {
|
|
124
|
-
'ds:X509Data': {
|
|
125
|
-
'ds:X509Certificate': '',
|
|
126
|
-
},
|
|
127
|
-
},
|
|
128
|
-
'ds:Object': {
|
|
129
|
-
'xades:QualifyingProperties': {
|
|
130
|
-
Target: 'signature',
|
|
131
|
-
'xades:SignedProperties': {
|
|
132
|
-
Target: 'signature',
|
|
133
|
-
Id: 'id-xades-signed-props',
|
|
134
|
-
'xades:SignedSignatureProperties': {
|
|
135
|
-
'xades:SigningTime': '',
|
|
136
|
-
'xades:SigningCertificate': {
|
|
137
|
-
'xades:Cert': {
|
|
138
|
-
'xades:CertDigest': {
|
|
139
|
-
'ds:DigestMethod': {
|
|
140
|
-
Algorithm: 'http://www.w3.org/2001/04/xmlenc#sha256',
|
|
141
|
-
},
|
|
142
|
-
'ds:DigestValue': '',
|
|
143
|
-
},
|
|
144
|
-
'xades:IssuerSerial': {
|
|
145
|
-
'ds:X509IssuerName': '',
|
|
146
|
-
'ds:X509SerialNumber': '',
|
|
147
|
-
},
|
|
148
|
-
},
|
|
149
|
-
},
|
|
150
|
-
},
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
},
|
|
154
|
-
},
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
describe('MyInvois Document Generation and Submission', () => {
|
|
159
|
-
const requiredEnvVars = [
|
|
160
|
-
'CLIENT_ID',
|
|
161
|
-
'CLIENT_SECRET',
|
|
162
|
-
'TEST_PRIVATE_KEY',
|
|
163
|
-
'TEST_CERTIFICATE',
|
|
164
|
-
]
|
|
165
|
-
|
|
166
|
-
// Check environment variables
|
|
167
|
-
const missingVars = requiredEnvVars.filter(varName => !process.env[varName])
|
|
168
|
-
|
|
169
|
-
if (missingVars.length > 0) {
|
|
170
|
-
it.skip(`Skipping tests - Missing environment variables: ${missingVars.join(', ')}`, () => {
|
|
171
|
-
expect
|
|
172
|
-
.soft(
|
|
173
|
-
false,
|
|
174
|
-
`Missing required environment variables: ${missingVars.join(', ')}`,
|
|
175
|
-
)
|
|
176
|
-
.toBe(true)
|
|
177
|
-
})
|
|
178
|
-
return
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
const CLIENT_ID = process.env.CLIENT_ID!
|
|
182
|
-
const CLIENT_SECRET = process.env.CLIENT_SECRET!
|
|
183
|
-
const PRIVATE_KEY = process.env.TEST_PRIVATE_KEY!
|
|
184
|
-
const CERTIFICATE = process.env.TEST_CERTIFICATE!
|
|
185
|
-
|
|
186
|
-
it('should extract certificate information correctly', () => {
|
|
187
|
-
console.log('🔍 Extracting certificate information...')
|
|
188
|
-
|
|
189
|
-
const certInfo = extractCertificateInfo(CERTIFICATE)
|
|
190
|
-
|
|
191
|
-
console.log('Certificate Info:', {
|
|
192
|
-
issuerName: certInfo.issuerName,
|
|
193
|
-
serialNumber: certInfo.serialNumber,
|
|
194
|
-
})
|
|
195
|
-
|
|
196
|
-
expect(certInfo.issuerName).toBeDefined()
|
|
197
|
-
expect(certInfo.serialNumber).toBeDefined()
|
|
198
|
-
expect(typeof certInfo.issuerName).toBe('string')
|
|
199
|
-
expect(typeof certInfo.serialNumber).toBe('string')
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
it('should find TIN that matches certificate', async () => {
|
|
203
|
-
console.log('🔐 Finding TIN that matches certificate...')
|
|
204
|
-
|
|
205
|
-
const client = new MyInvoisClient(
|
|
206
|
-
CLIENT_ID,
|
|
207
|
-
CLIENT_SECRET,
|
|
208
|
-
'sandbox',
|
|
209
|
-
CERTIFICATE,
|
|
210
|
-
PRIVATE_KEY,
|
|
211
|
-
undefined,
|
|
212
|
-
true, // debug mode
|
|
213
|
-
)
|
|
214
|
-
|
|
215
|
-
// Test TIN formats that might match the certificate
|
|
216
|
-
// Note: These are example TIN formats - replace with your actual test TINs
|
|
217
|
-
const testTINs = [
|
|
218
|
-
process.env.TIN_VALUE, // Environment variable if set
|
|
219
|
-
process.env.TEST_TIN_1, // Additional test TIN from environment
|
|
220
|
-
process.env.TEST_TIN_2, // Additional test TIN from environment
|
|
221
|
-
process.env.TEST_TIN_3, // Additional test TIN from environment
|
|
222
|
-
'EI00000000010', // Consolidated buyer (shouldn't work for supplier)
|
|
223
|
-
].filter(Boolean) // Remove undefined values
|
|
224
|
-
|
|
225
|
-
let certificateMatchingTIN: string | null = null
|
|
226
|
-
const validTINs: string[] = []
|
|
227
|
-
|
|
228
|
-
// Step 1: Check which TINs are valid in the system
|
|
229
|
-
console.log('📋 Step 1: Checking TIN validity...')
|
|
230
|
-
for (const tin of testTINs) {
|
|
231
|
-
try {
|
|
232
|
-
console.log(`Testing TIN validity: ${tin}`)
|
|
233
|
-
const isValid = await client.verifyTin(tin!, 'BRN', '123456789012')
|
|
234
|
-
console.log(`TIN ${tin} validity: ${isValid}`)
|
|
235
|
-
|
|
236
|
-
if (isValid) {
|
|
237
|
-
validTINs.push(tin!)
|
|
238
|
-
}
|
|
239
|
-
} catch (error) {
|
|
240
|
-
console.log(`❌ Error testing TIN ${tin}:`, error)
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
console.log(`📊 Found ${validTINs.length} valid TINs:`, validTINs)
|
|
245
|
-
|
|
246
|
-
// Step 2: Test each valid TIN with a minimal document submission to find certificate match
|
|
247
|
-
console.log('📋 Step 2: Testing certificate matching...')
|
|
248
|
-
for (const tin of validTINs) {
|
|
249
|
-
try {
|
|
250
|
-
console.log(`Testing TIN with certificate: ${tin}`)
|
|
251
|
-
|
|
252
|
-
// Create a minimal test invoice with this TIN
|
|
253
|
-
const testInvoice = createMinimalTestInvoice()
|
|
254
|
-
testInvoice.supplier.tin = tin
|
|
255
|
-
testInvoice.eInvoiceCodeOrNumber = `TEST-CERT-${Date.now()}`
|
|
256
|
-
|
|
257
|
-
// Try to submit it
|
|
258
|
-
const { status } = await client.submitDocument([testInvoice])
|
|
259
|
-
|
|
260
|
-
if (status === 202) {
|
|
261
|
-
console.log(`✅ SUCCESS! TIN ${tin} works with certificate`)
|
|
262
|
-
certificateMatchingTIN = tin
|
|
263
|
-
break
|
|
264
|
-
} else {
|
|
265
|
-
console.log(`❌ TIN ${tin} failed with status ${status}`)
|
|
266
|
-
}
|
|
267
|
-
} catch (error: any) {
|
|
268
|
-
if (
|
|
269
|
-
error.message?.includes(
|
|
270
|
-
'authenticated TIN and documents TIN is not matching',
|
|
271
|
-
)
|
|
272
|
-
) {
|
|
273
|
-
console.log(`❌ TIN ${tin} doesn't match certificate`)
|
|
274
|
-
} else {
|
|
275
|
-
console.log(`❌ TIN ${tin} failed with error:`, error.message)
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
// Store the certificate-matching TIN for next test
|
|
281
|
-
if (certificateMatchingTIN) {
|
|
282
|
-
process.env.VALID_SUPPLIER_TIN = certificateMatchingTIN
|
|
283
|
-
console.log(
|
|
284
|
-
`🎯 Certificate matching TIN found: ${certificateMatchingTIN}`,
|
|
285
|
-
)
|
|
286
|
-
} else if (validTINs.length > 0) {
|
|
287
|
-
// Fallback to first valid TIN with a warning
|
|
288
|
-
process.env.VALID_SUPPLIER_TIN = validTINs[0]
|
|
289
|
-
console.log(
|
|
290
|
-
`⚠️ No certificate match found, using first valid TIN: ${validTINs[0]}`,
|
|
291
|
-
)
|
|
292
|
-
console.log(
|
|
293
|
-
`⚠️ This will likely cause TIN mismatch error in submission test`,
|
|
294
|
-
)
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// For now, pass the test if we found any valid TINs (the main goal is to test the implementation)
|
|
298
|
-
expect.soft(validTINs.length, 'No valid TINs found').toBeGreaterThan(0)
|
|
299
|
-
}, 60000) // Increased timeout for multiple submissions
|
|
300
|
-
|
|
301
|
-
it('should generate valid document structure', () => {
|
|
302
|
-
console.log('📄 Generating document structure...')
|
|
303
|
-
|
|
304
|
-
const invoice = createMinimalTestInvoice()
|
|
305
|
-
|
|
306
|
-
// Use the valid TIN from previous test or fallback
|
|
307
|
-
const supplierTIN =
|
|
308
|
-
process.env.VALID_SUPPLIER_TIN || process.env.TIN_VALUE || 'C00000000000' // Replace with your TIN
|
|
309
|
-
invoice.supplier.tin = supplierTIN
|
|
310
|
-
|
|
311
|
-
console.log(`Using supplier TIN: ${supplierTIN}`)
|
|
312
|
-
|
|
313
|
-
const certInfo = extractCertificateInfo(CERTIFICATE)
|
|
314
|
-
|
|
315
|
-
const document = generateCompleteDocument([invoice], {
|
|
316
|
-
privateKeyPem: PRIVATE_KEY,
|
|
317
|
-
certificatePem: CERTIFICATE,
|
|
318
|
-
issuerName: certInfo.issuerName,
|
|
319
|
-
serialNumber: certInfo.serialNumber,
|
|
320
|
-
})
|
|
321
|
-
|
|
322
|
-
console.log('Generated document structure:')
|
|
323
|
-
console.log(
|
|
324
|
-
'- Namespace declarations:',
|
|
325
|
-
Object.keys(document).filter(k => k.startsWith('_')),
|
|
326
|
-
)
|
|
327
|
-
console.log('- Number of invoices:', document.Invoice.length)
|
|
328
|
-
console.log('- First invoice keys:', Object.keys(document.Invoice[0]))
|
|
329
|
-
console.log('- Has UBLExtensions:', !!document.Invoice[0].UBLExtensions)
|
|
330
|
-
console.log('- Has Signature:', !!document.Invoice[0].Signature)
|
|
331
|
-
|
|
332
|
-
// Basic structure validation
|
|
333
|
-
expect(document._D).toBe(
|
|
334
|
-
'urn:oasis:names:specification:ubl:schema:xsd:Invoice-2',
|
|
335
|
-
)
|
|
336
|
-
expect(document._A).toBe(
|
|
337
|
-
'urn:oasis:names:specification:ubl:schema:xsd:CommonAggregateComponents-2',
|
|
338
|
-
)
|
|
339
|
-
expect(document._B).toBe(
|
|
340
|
-
'urn:oasis:names:specification:ubl:schema:xsd:CommonBasicComponents-2',
|
|
341
|
-
)
|
|
342
|
-
expect(document.Invoice).toHaveLength(1)
|
|
343
|
-
expect(document.Invoice[0].UBLExtensions).toBeDefined()
|
|
344
|
-
expect(document.Invoice[0].Signature).toBeDefined()
|
|
345
|
-
|
|
346
|
-
// Store document for next test
|
|
347
|
-
;(globalThis as any).testDocument = document
|
|
348
|
-
;(globalThis as any).testInvoice = invoice
|
|
349
|
-
})
|
|
350
|
-
|
|
351
|
-
it('should submit document to MyInvois successfully', async () => {
|
|
352
|
-
console.log('🚀 Submitting document to MyInvois...')
|
|
353
|
-
|
|
354
|
-
const document = (globalThis as any).testDocument
|
|
355
|
-
const invoice = (globalThis as any).testInvoice
|
|
356
|
-
|
|
357
|
-
if (!document || !invoice) {
|
|
358
|
-
throw new Error('Document not generated in previous test')
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
const client = new MyInvoisClient(
|
|
362
|
-
CLIENT_ID,
|
|
363
|
-
CLIENT_SECRET,
|
|
364
|
-
'sandbox',
|
|
365
|
-
CERTIFICATE,
|
|
366
|
-
PRIVATE_KEY,
|
|
367
|
-
undefined,
|
|
368
|
-
true, // debug mode
|
|
369
|
-
)
|
|
370
|
-
|
|
371
|
-
try {
|
|
372
|
-
console.log('📤 Submitting to MyInvois API...')
|
|
373
|
-
console.log('Document size:', JSON.stringify(document).length, 'bytes')
|
|
374
|
-
|
|
375
|
-
const { data: response, status } = await client.submitDocument([invoice])
|
|
376
|
-
|
|
377
|
-
console.log('📥 Response received:')
|
|
378
|
-
console.log('Status:', status)
|
|
379
|
-
console.log('Response:', JSON.stringify(response, null, 2))
|
|
380
|
-
|
|
381
|
-
// Validate response structure
|
|
382
|
-
expect(status).toBe(202) // MyInvois returns 202 Accepted
|
|
383
|
-
expect(response.submissionUid).toBeDefined()
|
|
384
|
-
expect(typeof response.submissionUid).toBe('string')
|
|
385
|
-
|
|
386
|
-
// Check for accepted/rejected documents
|
|
387
|
-
if (response.acceptedDocuments?.length > 0) {
|
|
388
|
-
console.log('✅ Documents accepted:', response.acceptedDocuments.length)
|
|
389
|
-
response.acceptedDocuments.forEach((doc, index) => {
|
|
390
|
-
console.log(` Document ${index + 1}:`, doc.invoiceCodeNumber)
|
|
391
|
-
})
|
|
392
|
-
}
|
|
393
|
-
|
|
394
|
-
if (response.rejectedDocuments?.length > 0) {
|
|
395
|
-
console.log('❌ Documents rejected:', response.rejectedDocuments.length)
|
|
396
|
-
response.rejectedDocuments.forEach((doc, index) => {
|
|
397
|
-
console.log(` Document ${index + 1}:`, doc.invoiceCodeNumber)
|
|
398
|
-
if (doc.error) {
|
|
399
|
-
console.log(` Error:`, doc.error.message)
|
|
400
|
-
if (doc.error.details) {
|
|
401
|
-
doc.error.details.forEach((detail, detailIndex) => {
|
|
402
|
-
console.log(` Detail ${detailIndex + 1}:`, detail.message)
|
|
403
|
-
})
|
|
404
|
-
}
|
|
405
|
-
}
|
|
406
|
-
})
|
|
407
|
-
}
|
|
408
|
-
|
|
409
|
-
// Get submission status
|
|
410
|
-
console.log('📊 Checking submission status...')
|
|
411
|
-
const submission = await client.getSubmissionStatus(
|
|
412
|
-
response.submissionUid,
|
|
413
|
-
)
|
|
414
|
-
console.log('Submission status:', submission?.status)
|
|
415
|
-
|
|
416
|
-
expect(submission).toBeDefined()
|
|
417
|
-
expect(['InProgress', 'Valid', 'PartiallyValid', 'Invalid']).toContain(
|
|
418
|
-
submission?.status,
|
|
419
|
-
)
|
|
420
|
-
} catch (error: any) {
|
|
421
|
-
console.error('💥 Submission failed:', error)
|
|
422
|
-
|
|
423
|
-
// Analyze common error types
|
|
424
|
-
if (
|
|
425
|
-
error.message?.includes(
|
|
426
|
-
'authenticated TIN and documents TIN is not matching',
|
|
427
|
-
)
|
|
428
|
-
) {
|
|
429
|
-
console.log('\n🔍 TIN MISMATCH ERROR DETECTED!')
|
|
430
|
-
console.log(
|
|
431
|
-
'This means the TIN in the certificate does not match the TIN in the invoice.',
|
|
432
|
-
)
|
|
433
|
-
console.log(
|
|
434
|
-
'The certificate is registered to a different TIN in MyInvois system.',
|
|
435
|
-
)
|
|
436
|
-
console.log(
|
|
437
|
-
'Solution: Get the correct TIN for your certificate or get a certificate for your TIN.',
|
|
438
|
-
)
|
|
439
|
-
} else if (error.message?.includes('Invalid structure')) {
|
|
440
|
-
console.log('\n🔍 INVALID STRUCTURE ERROR DETECTED!')
|
|
441
|
-
console.log('This could be due to:')
|
|
442
|
-
console.log('1. Missing mandatory fields')
|
|
443
|
-
console.log('2. Incorrect field values')
|
|
444
|
-
console.log('3. Wrong data types')
|
|
445
|
-
console.log('4. Business rule violations')
|
|
446
|
-
}
|
|
447
|
-
|
|
448
|
-
// Re-throw to fail the test
|
|
449
|
-
throw error
|
|
450
|
-
}
|
|
451
|
-
}, 60000) // Extended timeout for API calls
|
|
452
|
-
})
|
|
@@ -1,130 +0,0 @@
|
|
|
1
|
-
import { describe, it } from 'vitest'
|
|
2
|
-
import type { InvoiceV1_1 } from '../src/types'
|
|
3
|
-
import {
|
|
4
|
-
diagnoseSignatureIssues,
|
|
5
|
-
printDiagnostics,
|
|
6
|
-
} from '../src/utils/signature-diagnostics'
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* ⚠️ SECURITY NOTICE: This file uses environment variables for sensitive data.
|
|
10
|
-
* Never hardcode actual TIN, NRIC, certificates, or API credentials in test files.
|
|
11
|
-
* Use .env file for your actual values (already gitignored).
|
|
12
|
-
*/
|
|
13
|
-
|
|
14
|
-
describe('Signature Diagnostics', () => {
|
|
15
|
-
it('should analyze current certificate and signature implementation', () => {
|
|
16
|
-
const CLIENT_ID = process.env.CLIENT_ID
|
|
17
|
-
const CLIENT_SECRET = process.env.CLIENT_SECRET
|
|
18
|
-
const PRIVATE_KEY = process.env.TEST_PRIVATE_KEY
|
|
19
|
-
const CERTIFICATE = process.env.TEST_CERTIFICATE
|
|
20
|
-
|
|
21
|
-
if (!CLIENT_ID || !CLIENT_SECRET || !PRIVATE_KEY || !CERTIFICATE) {
|
|
22
|
-
console.log('⚠️ Skipping diagnostics - Missing environment variables')
|
|
23
|
-
return
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Create test invoice
|
|
27
|
-
const testInvoice: InvoiceV1_1 = {
|
|
28
|
-
eInvoiceVersion: '1.1',
|
|
29
|
-
eInvoiceTypeCode: '01',
|
|
30
|
-
eInvoiceCodeOrNumber: `DIAG-${Date.now()}`,
|
|
31
|
-
eInvoiceDate: new Date().toISOString().split('T')[0],
|
|
32
|
-
eInvoiceTime: new Date().toISOString().split('T')[1].split('.')[0] + 'Z',
|
|
33
|
-
invoiceCurrencyCode: 'MYR',
|
|
34
|
-
|
|
35
|
-
supplier: {
|
|
36
|
-
name: 'Test Company Sdn Bhd',
|
|
37
|
-
tin: process.env.TIN_VALUE || 'IG00000000000', // Replace with your TIN
|
|
38
|
-
registrationType: 'NRIC',
|
|
39
|
-
registrationNumber: process.env.NRIC_VALUE || '123456789012',
|
|
40
|
-
contactNumber: '+60123456789',
|
|
41
|
-
address: {
|
|
42
|
-
addressLine0: '123 Test Street',
|
|
43
|
-
cityName: 'Kuala Lumpur',
|
|
44
|
-
state: '14',
|
|
45
|
-
country: 'MYS',
|
|
46
|
-
},
|
|
47
|
-
industryClassificationCode: '41001',
|
|
48
|
-
industryClassificationDescription: 'Test Industry',
|
|
49
|
-
},
|
|
50
|
-
|
|
51
|
-
buyer: {
|
|
52
|
-
name: 'CONSOLIDATED E-INVOICE BUYER',
|
|
53
|
-
tin: 'EI00000000010',
|
|
54
|
-
registrationType: 'NRIC',
|
|
55
|
-
registrationNumber: process.env.BUYER_NRIC_VALUE || '000000000000', // Replace with test NRIC
|
|
56
|
-
sstRegistrationNumber: 'NA',
|
|
57
|
-
contactNumber: '+60123456789',
|
|
58
|
-
address: {
|
|
59
|
-
addressLine0: 'NA',
|
|
60
|
-
cityName: 'KUALA LUMPUR',
|
|
61
|
-
state: '14',
|
|
62
|
-
country: 'MYS',
|
|
63
|
-
},
|
|
64
|
-
},
|
|
65
|
-
|
|
66
|
-
invoiceLineItems: [
|
|
67
|
-
{
|
|
68
|
-
itemClassificationCode: '001',
|
|
69
|
-
itemDescription: 'Test Product',
|
|
70
|
-
unitPrice: 100.0,
|
|
71
|
-
taxType: '01',
|
|
72
|
-
taxRate: 6.0,
|
|
73
|
-
taxAmount: 6.0,
|
|
74
|
-
totalTaxableAmountPerLine: 100.0,
|
|
75
|
-
totalAmountPerLine: 106.0,
|
|
76
|
-
},
|
|
77
|
-
],
|
|
78
|
-
|
|
79
|
-
legalMonetaryTotal: {
|
|
80
|
-
taxExclusiveAmount: 100.0,
|
|
81
|
-
taxInclusiveAmount: 106.0,
|
|
82
|
-
payableAmount: 106.0,
|
|
83
|
-
},
|
|
84
|
-
|
|
85
|
-
taxTotal: {
|
|
86
|
-
taxAmount: 6.0,
|
|
87
|
-
},
|
|
88
|
-
|
|
89
|
-
issuerDigitalSignature: {} as any,
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
console.log('🔍 Running MyInvois Signature Diagnostics...\n')
|
|
93
|
-
|
|
94
|
-
try {
|
|
95
|
-
const diagnosticResult = diagnoseSignatureIssues(
|
|
96
|
-
[testInvoice],
|
|
97
|
-
CERTIFICATE,
|
|
98
|
-
)
|
|
99
|
-
|
|
100
|
-
printDiagnostics(diagnosticResult)
|
|
101
|
-
|
|
102
|
-
// Summary
|
|
103
|
-
const certificateIssues =
|
|
104
|
-
diagnosticResult.certificateAnalysis.issues.length
|
|
105
|
-
const signatureIssues = diagnosticResult.signatureAnalysis.issues.length
|
|
106
|
-
|
|
107
|
-
console.log('\n📊 SUMMARY:')
|
|
108
|
-
console.log(` Certificate Issues: ${certificateIssues}`)
|
|
109
|
-
console.log(` Signature Issues: ${signatureIssues}`)
|
|
110
|
-
console.log(` Total Issues: ${certificateIssues + signatureIssues}`)
|
|
111
|
-
|
|
112
|
-
if (certificateIssues > 0) {
|
|
113
|
-
console.log('\n🚨 PRIMARY ACTION REQUIRED:')
|
|
114
|
-
console.log(
|
|
115
|
-
' Obtain official business certificate from MyInvois-approved CA',
|
|
116
|
-
)
|
|
117
|
-
console.log(
|
|
118
|
-
' Self-generated certificates cannot pass MyInvois validation',
|
|
119
|
-
)
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (signatureIssues > 0) {
|
|
123
|
-
console.log('\n⚙️ SECONDARY ACTION:')
|
|
124
|
-
console.log(' Review and optimize signature implementation')
|
|
125
|
-
}
|
|
126
|
-
} catch (error) {
|
|
127
|
-
console.error('❌ Diagnostic failed:', error)
|
|
128
|
-
}
|
|
129
|
-
})
|
|
130
|
-
})
|
package/tsconfig.json
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
/* Base Options: */
|
|
4
|
-
"esModuleInterop": true,
|
|
5
|
-
"skipLibCheck": false,
|
|
6
|
-
"target": "ESNext",
|
|
7
|
-
"allowJs": false,
|
|
8
|
-
"resolveJsonModule": true,
|
|
9
|
-
"moduleDetection": "force",
|
|
10
|
-
"isolatedModules": true, // Often recommended for bundlers
|
|
11
|
-
"composite": true,
|
|
12
|
-
/* Strictness */
|
|
13
|
-
"strict": true,
|
|
14
|
-
"noUncheckedIndexedAccess": true,
|
|
15
|
-
"noImplicitAny": true,
|
|
16
|
-
/* Bundler Behavior */
|
|
17
|
-
"moduleResolution": "Bundler", // Or "NodeNext"
|
|
18
|
-
"module": "ESNext",
|
|
19
|
-
"noEmit": true, // Rolldown handles emission
|
|
20
|
-
/* Path Resolution */
|
|
21
|
-
"baseUrl": ".", // Important for resolving paths relative to root
|
|
22
|
-
/* Type Checking */
|
|
23
|
-
"types": [
|
|
24
|
-
"bun"
|
|
25
|
-
] // Include Bun types if using Bun runtime features
|
|
26
|
-
},
|
|
27
|
-
"include": [
|
|
28
|
-
"src/**/*.ts",
|
|
29
|
-
"src/**/*.d.ts",
|
|
30
|
-
"rolldown.config.ts",
|
|
31
|
-
"test/MyInvoisClient.test.ts",
|
|
32
|
-
"test/base64.test.ts",
|
|
33
|
-
"test/transform.test.ts"
|
|
34
|
-
],
|
|
35
|
-
"exclude": [
|
|
36
|
-
"node_modules",
|
|
37
|
-
"dist"
|
|
38
|
-
]
|
|
39
|
-
}
|
package/tsdown.config.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import { defineConfig } from 'tsdown'
|
|
2
|
-
|
|
3
|
-
const external = [
|
|
4
|
-
'xmldom-ts',
|
|
5
|
-
'xpath-ts',
|
|
6
|
-
'xml-c14n',
|
|
7
|
-
'xml-crypto',
|
|
8
|
-
'crypto',
|
|
9
|
-
'@xmldom/xmldom',
|
|
10
|
-
]
|
|
11
|
-
|
|
12
|
-
export default [
|
|
13
|
-
defineConfig({
|
|
14
|
-
entry: ['./src'],
|
|
15
|
-
dts: {
|
|
16
|
-
isolatedDeclarations: true,
|
|
17
|
-
},
|
|
18
|
-
external,
|
|
19
|
-
}),
|
|
20
|
-
defineConfig({
|
|
21
|
-
entry: ['./src'],
|
|
22
|
-
format: 'cjs',
|
|
23
|
-
sourcemap: true,
|
|
24
|
-
platform: 'node',
|
|
25
|
-
outputOptions: {
|
|
26
|
-
entryFileNames: 'index.cjs',
|
|
27
|
-
chunkFileNames: '[name]-[hash].cjs',
|
|
28
|
-
},
|
|
29
|
-
external,
|
|
30
|
-
}),
|
|
31
|
-
]
|