@notabene/javascript-sdk 2.13.0 → 2.14.0-next.3

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 CHANGED
@@ -10,7 +10,7 @@
10
10
  "author": "Notabene <developers@notabene.id>",
11
11
  "license": "MIT",
12
12
  "packageManager": "yarn@4.5.1",
13
- "version": "2.13.0",
13
+ "version": "2.14.0-next.3",
14
14
  "source": "src/notabene.ts",
15
15
  "main": "dist/cjs/notabene.cjs",
16
16
  "module": "dist/esm/notabene.js",
@@ -32,7 +32,7 @@
32
32
  "import": "./dist/esm/notabene.js",
33
33
  "require": "./dist/cjs/notabene.cjs"
34
34
  },
35
- "./src/ivms/types": "./src/ivms/types.ts"
35
+ "./src/ivms/types": "./src/ivms/index.ts"
36
36
  },
37
37
  "browserslist": "> 0.5%, last 2 versions, not dead",
38
38
  "publishConfig": {
@@ -104,5 +104,9 @@
104
104
  "extends": [
105
105
  "@commitlint/config-conventional"
106
106
  ]
107
+ },
108
+ "dependencies": {
109
+ "@taprsvp/types": "^1.13.0",
110
+ "jose": "^6.1.0"
107
111
  }
108
112
  }
@@ -0,0 +1,5 @@
1
+ // Re-export all types from types.ts
2
+ export * from './types';
3
+
4
+ // Re-export all V2 types
5
+ export * from './v2Types';
@@ -0,0 +1,35 @@
1
+ import {
2
+ NaturalPerson,
3
+ NaturalPersonName,
4
+ NaturalPersonNameTypeCode,
5
+ Person,
6
+ } from './types';
7
+
8
+ export type OriginatorV2 = {
9
+ originatorPerson: PersonV2[];
10
+ customerIdentification?: string;
11
+ };
12
+
13
+ export type BeneficiaryV2 = {
14
+ beneficiaryPerson: PersonV2[];
15
+ customerIdentification?: string;
16
+ };
17
+
18
+ export type NaturalPersonNameIDV2 = {
19
+ primaryIdentifier?: string;
20
+ secondaryIdentifier?: string;
21
+ naturalPersonNameIdentifierType?: NaturalPersonNameTypeCode;
22
+ };
23
+
24
+ export type NaturalPersonNameV2 = Omit<NaturalPersonName, 'nameIdentifier'> & {
25
+ nameIdentifier?: NaturalPersonNameIDV2[];
26
+ };
27
+
28
+ export type NaturalPersonV2 = Omit<NaturalPerson, 'name'> & {
29
+ name: NaturalPersonNameV2;
30
+ };
31
+
32
+ export type PersonV2 = Omit<Person, 'naturalPerson'> & {
33
+ naturalPerson?: NaturalPersonV2;
34
+ accountNumber?: string[];
35
+ };
package/src/notabene.ts CHANGED
@@ -83,6 +83,18 @@ import { type MessageCallback } from './utils/MessageEventManager';
83
83
  import { decodeFragmentToObject, encodeObjectToFragment } from './utils/urls';
84
84
  // Must be exported for React Native SDK to use
85
85
  export { default as EmbeddedComponent } from './components/EmbeddedComponent';
86
+ export {
87
+ componentResponseToIVMS101,
88
+ componentResponseToTxCreateRequest,
89
+ componentResponseToTxRequests,
90
+ componentResponseToV1TxCreateRequest,
91
+ } from './responseTransformer';
92
+ export type {
93
+ ResponseToTxRequestConfig,
94
+ TransactionCreateRequest,
95
+ TransactionCreateRequestV2,
96
+ TransactionIVMS101Request,
97
+ } from './responseTransformer';
86
98
  export {
87
99
  ConnectionManager,
88
100
  getRefreshResult,
@@ -0,0 +1,164 @@
1
+ # Notabene TX Transformer
2
+
3
+ A utility module to transform Notabene component responses into API request bodies for Version 1 and Version 2 APIs.
4
+
5
+ ## Usage
6
+
7
+ ### Version 2 API - Complete Workflow
8
+
9
+ ```typescript
10
+ import { componentResponseToTxRequests, EXAMPLE_ORIGINATOR_V2 } from '$lib/notabene-tx-transformer';
11
+
12
+ withdrawal.on('complete', async (result) => {
13
+ // Generate all request bodies in one call
14
+ const { createTx, appendPii, confirmRelationship } = componentResponseToTxRequests(
15
+ result.response,
16
+ delegateToken,
17
+ { originator: EXAMPLE_ORIGINATOR_V2 }
18
+ );
19
+
20
+ // 1. Create transaction
21
+ const txResponse = await fetch(`/entity/${vaspDid}/tx`, {
22
+ method: 'POST',
23
+ body: JSON.stringify(createTx)
24
+ });
25
+
26
+ const txId = txResponse.transfer['@id'];
27
+
28
+ // 2. Append PII
29
+ await fetch(`/entity/${vaspDid}/tx/${txId}/append`, {
30
+ method: 'POST',
31
+ body: JSON.stringify(appendPii)
32
+ });
33
+
34
+ // 3. Confirm relationship (if proof exists)
35
+ if (confirmRelationship) {
36
+ await fetch(`/entity/${vaspDid}/relationship?to=...&from=...`, {
37
+ method: 'PATCH',
38
+ body: JSON.stringify(confirmRelationship)
39
+ });
40
+ }
41
+ });
42
+ ```
43
+
44
+ ### Version 2 API - Individual Functions
45
+
46
+ ```typescript
47
+ import {
48
+ componentResponseToTxCreateRequest,
49
+ componentResponseToIVMS101
50
+ } from '$lib/notabene-tx-transformer';
51
+
52
+ // Create transaction only
53
+ const createBody = componentResponseToTxCreateRequest(response, delegateToken);
54
+
55
+ // IVMS101 data only
56
+ const ivms101Body = componentResponseToIVMS101(response, delegateToken, {
57
+ originator: EXAMPLE_ORIGINATOR_V2
58
+ });
59
+ ```
60
+
61
+ ### Version 1 API (Legacy)
62
+
63
+ ```typescript
64
+ import { componentResponseToV1TxCreateRequest } from '$lib/notabene-tx-transformer';
65
+
66
+ withdrawal.on('complete', async (result) => {
67
+ const requestBody = componentResponseToV1TxCreateRequest(result.response);
68
+
69
+ // Send to your Version 1 API
70
+ await fetch(`${apiUrlV1}/entity/${did}/tx`, {
71
+ method: 'POST',
72
+ headers: {
73
+ 'Content-Type': 'application/json',
74
+ Authorization: `Bearer ${token}`
75
+ },
76
+ body: JSON.stringify(requestBody)
77
+ });
78
+ });
79
+ ```
80
+
81
+ ## API
82
+
83
+ ### `componentResponseToTxRequests(response, delegateToken, config?)`
84
+
85
+ Transforms a Notabene component response into all V2 request bodies (createTx, appendPii, and optionally confirmRelationship).
86
+
87
+ **Parameters:**
88
+
89
+ - `response`: Response from the Notabene TX Create component
90
+ - `delegateToken`: JWT delegate token for extracting the originator ID
91
+ - `config`: Optional `ResponseToTxRequestConfig` object
92
+ - `config.originatorId`: Auto-extracted from delegateToken if not provided
93
+ - `config.beneficiaryId`: Auto-generated from destination if not provided
94
+ - `config.referenceId`: Optional reference ID for the transaction
95
+ - `config.originator`: Optional originator information in V2 format for PII append
96
+
97
+ **Returns:** Object with `createTx`, `appendPii`, and optionally `confirmRelationship`
98
+
99
+ ### `componentResponseToTxCreateRequest(response, delegateToken, config?)`
100
+
101
+ Transforms a Notabene component response to a V2 transaction create request.
102
+
103
+ **Parameters:**
104
+
105
+ - `response`: Response from the Notabene TX Create component
106
+ - `delegateToken`: JWT delegate token for extracting the originator ID
107
+ - `config`: Optional configuration object
108
+ - `config.originatorId`: Auto-extracted from delegateToken if not provided
109
+ - `config.beneficiaryId`: Auto-generated from destination if not provided
110
+ - `config.referenceId`: Optional reference ID for the transaction
111
+
112
+ **Returns:** Transaction create request body
113
+
114
+ ### `componentResponseToIVMS101(response, delegateToken, config?)`
115
+
116
+ Transforms a Notabene component response to IVMS101 format.
117
+
118
+ **Parameters:**
119
+
120
+ - `response`: Response from the Notabene TX Create component
121
+ - `delegateToken`: JWT delegate token for extracting the originator ID
122
+ - `config`: Optional `ResponseToTxRequestConfig` object
123
+ - `config.originatorId`: Auto-extracted from delegateToken if not provided
124
+ - `config.beneficiaryId`: Auto-generated from destination if not provided
125
+ - `config.referenceId`: Optional reference ID for the transaction
126
+ - `config.originator`: Optional originator information in V2 format
127
+
128
+ **Returns:** IVMS101 formatted request body
129
+
130
+ ### `componentResponseToV1TxCreateRequest(response)`
131
+
132
+ Transforms a Notabene component response to a V1 transaction create request.
133
+
134
+ **Parameters:**
135
+
136
+ - `response`: Response from the Notabene TX Create component
137
+
138
+ **Returns:** V1 transaction create request body
139
+
140
+ ## Module Structure
141
+
142
+ ```
143
+ notabene-tx-transformer/
144
+ ├── index.ts # Main exports
145
+ ├── transformer.ts # Main transformation function
146
+ ├── mappers.ts # V1 and V2 mapping logic
147
+ ├── types.ts # Types not available in SDK
148
+ ├── utils.ts # Utility functions (getPartyId)
149
+ └── README.md # This file
150
+ ```
151
+
152
+ ## Configuration
153
+
154
+ All V2 functions accept an optional `ResponseToTxRequestConfig` parameter with auto-enrichment:
155
+
156
+ - **`originatorId`**: Auto-extracted from `delegateToken.sub` if not provided
157
+ - **`beneficiaryId`**: Auto-generated as `did:key:${destination}` if not provided
158
+ - **`referenceId`**: Optional transaction reference
159
+ - **`originator`**: Optional originator information in V2 format (used in PII append requests)
160
+
161
+ ## Version Differences
162
+
163
+ - **Version 1 (Legacy)**: Single-step transaction creation with embedded PII
164
+ - **Version 2**: Multi-step workflow (create → append PII → confirm relationship)
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Notabene TX Transformer
3
+ *
4
+ * A utility module to transform Notabene component responses into API request bodies.
5
+ */
6
+
7
+ // Main export
8
+ export {
9
+ componentResponseToIVMS101,
10
+ componentResponseToTxCreateRequest,
11
+ componentResponseToTxRequests,
12
+ componentResponseToV1TxCreateRequest,
13
+ } from './transformer';
14
+
15
+ // Type exports
16
+ export {
17
+ type ResponseToTxRequestConfig,
18
+ type TransactionCreateRequest,
19
+ type TransactionCreateRequestV2, // rename to TransactCreateRequest?
20
+ type TransactionIVMS101Request,
21
+ } from './types';
@@ -0,0 +1,293 @@
1
+ import type {
2
+ Beneficiary,
3
+ NaturalPerson,
4
+ NaturalPersonName,
5
+ Originator,
6
+ Person,
7
+ } from '@notabene/javascript-sdk/src/ivms/types';
8
+ import type { Agent } from '@taprsvp/types';
9
+ import type { IVMS101, V1Transaction, Withdrawal } from '../types';
10
+ import type {
11
+ ResponseToTxRequestConfig,
12
+ TransactionCreateRequest,
13
+ TransactionCreateRequestV2,
14
+ TransactionIVMS101Request,
15
+ } from './types';
16
+ import { convertPersonToV2, getPartyId } from './utils';
17
+
18
+ // Constants
19
+ const DEFAULT_GEOGRAPHIC_ADDRESS = [
20
+ {
21
+ addressType: 'GEOG' as const,
22
+ addressLine: ['1234 Main Street'],
23
+ townName: 'Unknown',
24
+ country: 'US',
25
+ },
26
+ ];
27
+
28
+ // Helper functions
29
+ function parseName(fullName: string) {
30
+ const trimmedName = fullName.trim();
31
+ if (!trimmedName) {
32
+ return { primaryIdentifier: '', secondaryIdentifier: '' };
33
+ }
34
+
35
+ const parts = trimmedName.split(/\s+/);
36
+ const primaryIdentifier = parts[parts.length - 1];
37
+ const secondaryParts = parts.slice(0, -1);
38
+ const secondaryIdentifier =
39
+ secondaryParts.length > 0 ? secondaryParts.join(' ') : primaryIdentifier;
40
+
41
+ return { primaryIdentifier, secondaryIdentifier };
42
+ }
43
+
44
+ function copyIVMS(
45
+ type: 'originator' | 'beneficiary',
46
+ persons?: Person[],
47
+ accountNumber: string[] = ['1234567890'],
48
+ ): Originator | Beneficiary {
49
+ const updatedPersons = persons?.map((person) => {
50
+ const updatedPerson = { ...person };
51
+
52
+ if (updatedPerson.naturalPerson) {
53
+ updatedPerson.naturalPerson = {
54
+ ...updatedPerson.naturalPerson,
55
+ geographicAddress: DEFAULT_GEOGRAPHIC_ADDRESS,
56
+ };
57
+ }
58
+
59
+ if (updatedPerson.legalPerson) {
60
+ updatedPerson.legalPerson = {
61
+ ...updatedPerson.legalPerson,
62
+ geographicAddress: DEFAULT_GEOGRAPHIC_ADDRESS,
63
+ };
64
+ }
65
+
66
+ return updatedPerson;
67
+ });
68
+
69
+ return type === 'originator'
70
+ ? { originatorPersons: updatedPersons, accountNumber }
71
+ : { beneficiaryPersons: updatedPersons, accountNumber };
72
+ }
73
+
74
+ function hostIVMS(
75
+ type: 'originator' | 'beneficiary',
76
+ name = 'John Doe',
77
+ ): Originator | Beneficiary {
78
+ const { primaryIdentifier, secondaryIdentifier } = parseName(name);
79
+
80
+ const person: Person = {
81
+ naturalPerson: {
82
+ name: [
83
+ {
84
+ nameIdentifier: [
85
+ {
86
+ primaryIdentifier,
87
+ ...(secondaryIdentifier ? { secondaryIdentifier } : {}),
88
+ nameIdentifierType: 'LEGL',
89
+ },
90
+ ],
91
+ },
92
+ ] as any,
93
+ geographicAddress: DEFAULT_GEOGRAPHIC_ADDRESS,
94
+ },
95
+ };
96
+
97
+ return type === 'originator'
98
+ ? { originatorPersons: [person], accountNumber: ['1234567890'] }
99
+ : { beneficiaryPersons: [person], accountNumber: ['1234567890'] };
100
+ }
101
+
102
+ const wrapNameInArray = (naturalPerson: NaturalPerson): NaturalPerson => ({
103
+ ...naturalPerson,
104
+ // @ts-expect-error Preserving the original logic
105
+ name: naturalPerson.name
106
+ ? ([naturalPerson.name] as NaturalPersonName[])
107
+ : undefined,
108
+ });
109
+
110
+ const transformPerson = (person: Person): Person => ({
111
+ ...person,
112
+ naturalPerson: person.naturalPerson
113
+ ? wrapNameInArray(person.naturalPerson)
114
+ : undefined,
115
+ });
116
+
117
+ const transformIVMS101 = (
118
+ { beneficiary, originator }: IVMS101,
119
+ name?: string,
120
+ originatorEqualsBeneficiary?: boolean,
121
+ destination?: string,
122
+ operation?: 'withdrawal' | 'deposit',
123
+ ) => {
124
+ if (operation === 'withdrawal') {
125
+ const transformedBeneficiary = beneficiary && {
126
+ ...beneficiary,
127
+ ...(destination && { accountNumber: [destination] }),
128
+ beneficiaryPersons: beneficiary.beneficiaryPersons?.map(transformPerson),
129
+ };
130
+
131
+ if (
132
+ originatorEqualsBeneficiary &&
133
+ transformedBeneficiary?.beneficiaryPersons
134
+ ) {
135
+ const originator = copyIVMS(
136
+ 'originator',
137
+ transformedBeneficiary?.beneficiaryPersons,
138
+ ) as Originator;
139
+
140
+ return {
141
+ beneficiary: {
142
+ accountNumber: originator.accountNumber,
143
+ beneficiaryPersons: originator.originatorPersons,
144
+ },
145
+ originator,
146
+ };
147
+ }
148
+
149
+ return {
150
+ beneficiary: transformedBeneficiary,
151
+ originator: hostIVMS('originator', name),
152
+ };
153
+ }
154
+
155
+ if (operation === 'deposit') {
156
+ const transformedOriginator = originator && {
157
+ ...(destination && { accountNumber: [destination] }),
158
+ originatorPersons: originator.originatorPersons?.map(transformPerson),
159
+ };
160
+
161
+ if (
162
+ originatorEqualsBeneficiary &&
163
+ transformedOriginator?.originatorPersons
164
+ ) {
165
+ const beneficiary = copyIVMS(
166
+ 'beneficiary',
167
+ transformedOriginator?.originatorPersons,
168
+ ) as Beneficiary;
169
+
170
+ return {
171
+ originator: {
172
+ accountNumber: beneficiary.accountNumber,
173
+ originatorPersons: beneficiary.beneficiaryPersons,
174
+ },
175
+ beneficiary,
176
+ };
177
+ }
178
+
179
+ return {
180
+ originator: transformedOriginator,
181
+ beneficiary: hostIVMS('beneficiary', name),
182
+ };
183
+ }
184
+
185
+ throw new Error('Invalid operation type');
186
+ };
187
+
188
+ export function mapToV1CreateRequest(
189
+ withdrawal: Withdrawal,
190
+ payload: V1Transaction,
191
+ ivms101: IVMS101,
192
+ ): TransactionCreateRequest {
193
+ const { beneficiary, originator } = transformIVMS101(
194
+ ivms101,
195
+ withdrawal.customer?.name,
196
+ payload.originatorEqualsBeneficiary,
197
+ withdrawal.destination,
198
+ 'withdrawal',
199
+ );
200
+
201
+ return {
202
+ transactionAsset: payload.transactionAsset,
203
+ transactionAmount: payload.transactionAmount,
204
+ beneficiaryDid: withdrawal.counterparty?.did,
205
+ originatorVASPdid: payload.originatorVASPdid,
206
+ ...(payload.beneficiaryVASPdid && !payload.beneficiaryProof
207
+ ? { beneficiaryVASPdid: payload.beneficiaryVASPdid }
208
+ : {}),
209
+ transactionBlockchainInfo: {
210
+ ...(originator?.accountNumber && { origin: originator.accountNumber[0] }),
211
+ ...(withdrawal.destination && { destination: withdrawal.destination }),
212
+ },
213
+ ...(payload.beneficiaryProof && {
214
+ beneficiaryProof: payload.beneficiaryProof,
215
+ }),
216
+ ...(beneficiary && { beneficiary }),
217
+ ...(originator && { originator }),
218
+ ...(payload.originatorEqualsBeneficiary && {
219
+ originatorEqualsBeneficiary: payload.originatorEqualsBeneficiary,
220
+ }),
221
+ };
222
+ }
223
+
224
+ export function mapToTransactCreateRequest(
225
+ withdrawal: Withdrawal,
226
+ payload: V1Transaction,
227
+ config: Omit<ResponseToTxRequestConfig, 'originator'> = {},
228
+ ): TransactionCreateRequestV2 {
229
+ const originatorId =
230
+ config?.originatorId || getPartyId('originator', withdrawal.customer);
231
+ const beneficiaryId =
232
+ config?.beneficiaryId || getPartyId('beneficiary', withdrawal.counterparty);
233
+ const referenceId =
234
+ config?.referenceId || Math.random().toString(36).substring(2, 15);
235
+ const agents: Agent[] = [];
236
+
237
+ if (payload.originatorVASPdid) {
238
+ agents.push({
239
+ '@id': payload.originatorVASPdid,
240
+ for: originatorId,
241
+ role: 'VASP',
242
+ });
243
+ }
244
+
245
+ if (payload.beneficiaryVASPdid) {
246
+ agents.push({
247
+ '@id': payload.beneficiaryVASPdid,
248
+ for: beneficiaryId,
249
+ role: 'VASP',
250
+ });
251
+ }
252
+
253
+ if (withdrawal.destination && withdrawal?.account?.did) {
254
+ agents.push({
255
+ '@id': withdrawal.account.did,
256
+ for: payload.beneficiaryVASPdid || beneficiaryId,
257
+ role: 'SettlementAddress',
258
+ });
259
+ }
260
+
261
+ return {
262
+ originator: { '@id': originatorId },
263
+ beneficiary: { '@id': beneficiaryId },
264
+ asset: withdrawal.asset,
265
+ amount: withdrawal.amountDecimal?.toString() || payload.transactionAmount,
266
+ agents,
267
+ ref: referenceId,
268
+ };
269
+ }
270
+
271
+ export function mapToAppendPiiRequest(
272
+ withdrawal: Withdrawal,
273
+ ivms101: IVMS101,
274
+ config: ResponseToTxRequestConfig = {},
275
+ ): TransactionIVMS101Request {
276
+ const beneficiaryId =
277
+ config.beneficiaryId || getPartyId('beneficiary', withdrawal.counterparty);
278
+
279
+ // Convert all beneficiary persons from V1 to V2 format
280
+ const beneficiaryPersons =
281
+ ivms101.beneficiary?.beneficiaryPersons?.map((person) =>
282
+ convertPersonToV2(person, beneficiaryId),
283
+ ) || [];
284
+
285
+ return {
286
+ ivms101: {
287
+ originator: config.originator,
288
+ beneficiary: {
289
+ beneficiaryPerson: beneficiaryPersons,
290
+ },
291
+ },
292
+ };
293
+ }