@notabene/javascript-sdk 2.18.0 → 2.19.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/dist/cjs/notabene.cjs +6 -6
- package/dist/cjs/notabene.d.ts +19 -45
- package/dist/cjs/package.json +1 -1
- package/dist/esm/notabene.d.ts +19 -45
- package/dist/esm/notabene.js +1525 -1651
- package/dist/esm/package.json +1 -1
- package/dist/notabene.d.ts +19 -45
- package/dist/notabene.js +1525 -1651
- package/docs/README.md +34 -1
- package/package.json +1 -1
- package/src/__tests__/EmbeddedComponent.portless-message.test.ts +66 -0
- package/src/__tests__/EmbeddedComponent.test.ts +3 -3
- package/src/components/EmbeddedComponent.ts +18 -8
- package/src/notabene.ts +2 -2
- package/src/responseTransformer/README.md +0 -30
- package/src/responseTransformer/__tests__/transformer.test.ts +2 -2
- package/src/responseTransformer/index.ts +1 -3
- package/src/responseTransformer/mappers.ts +9 -218
- package/src/responseTransformer/transformer.ts +1 -57
- package/src/responseTransformer/types.ts +0 -18
- package/src/types.ts +21 -6
- package/src/utils/MessageEventManager.ts +2 -0
- package/src/utils/__tests__/MessageEventManager.test.ts +8 -0
package/docs/README.md
CHANGED
|
@@ -73,6 +73,7 @@ This library is the JavaScript SDK for loading the Notabene UX components in the
|
|
|
73
73
|
- [Full Example](#full-example)
|
|
74
74
|
- [Field reference](#field-reference)
|
|
75
75
|
- [Configure Counterparty Assist](#counterparty-assist-configuration)
|
|
76
|
+
- [Consent / Terms & Conditions](#consent--terms--conditions)
|
|
76
77
|
- [Locales](#locales)
|
|
77
78
|
- [Theming](#theming)
|
|
78
79
|
- [License](#license)
|
|
@@ -767,7 +768,8 @@ const options: TransactionOptions = {
|
|
|
767
768
|
PersonType.NATURAL, // JS: 'natural'
|
|
768
769
|
PersonType.SELF, // JS: 'self'
|
|
769
770
|
],
|
|
770
|
-
}
|
|
771
|
+
},
|
|
772
|
+
consent: true, // true (default) | false (hide) | { label?: string, description?: string }
|
|
771
773
|
};
|
|
772
774
|
const withdrawal = notabene.createWithdrawalAssist(tx, options);
|
|
773
775
|
```
|
|
@@ -987,6 +989,37 @@ const options: TransactionOptions = {
|
|
|
987
989
|
| `lei` | -- | ✅ | 🟩 | LEI (Legal Entity Identifier) |
|
|
988
990
|
| `countryOfRegistration` | -- | 🟩 | 🟩 | Country of Registration |
|
|
989
991
|
|
|
992
|
+
### Consent / Terms & Conditions
|
|
993
|
+
|
|
994
|
+
By default, a consent checkbox is shown when the counterparty agent is a VASP. It asks the user to accept sharing personal information with the receiving/sending exchange. You can configure this behavior using the `consent` option.
|
|
995
|
+
|
|
996
|
+
- `undefined` or `true`: Show the default consent checkbox (default behavior)
|
|
997
|
+
- `false`: Hide the consent checkbox entirely
|
|
998
|
+
- `{ label?, description? }`: Show the checkbox with custom text
|
|
999
|
+
|
|
1000
|
+
#### Hiding the consent checkbox
|
|
1001
|
+
|
|
1002
|
+
```ts
|
|
1003
|
+
const options: TransactionOptions = {
|
|
1004
|
+
consent: false,
|
|
1005
|
+
};
|
|
1006
|
+
```
|
|
1007
|
+
|
|
1008
|
+
When hidden, the consent requirement is skipped in validation — the component will complete without requiring the user to check the box.
|
|
1009
|
+
|
|
1010
|
+
#### Custom consent text
|
|
1011
|
+
|
|
1012
|
+
```ts
|
|
1013
|
+
const options: TransactionOptions = {
|
|
1014
|
+
consent: {
|
|
1015
|
+
label: 'I agree to the terms',
|
|
1016
|
+
description: 'By proceeding you agree to share your name and address with the counterparty.',
|
|
1017
|
+
},
|
|
1018
|
+
};
|
|
1019
|
+
```
|
|
1020
|
+
|
|
1021
|
+
Both `label` and `description` are optional — you can override one while keeping the default for the other. Translations are the customer's responsibility when providing custom text.
|
|
1022
|
+
|
|
990
1023
|
## Locales
|
|
991
1024
|
|
|
992
1025
|
See [locales](_media/locales.ts) for the list of supported locales.
|
package/package.json
CHANGED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, test } from 'vitest';
|
|
2
|
+
import EmbeddedComponent from '../components/EmbeddedComponent';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Integration test (no mocks) reproducing the Banxa crash (PAY-1056):
|
|
6
|
+
*
|
|
7
|
+
* TypeError: Cannot set properties of undefined (setting 'onmessage')
|
|
8
|
+
*
|
|
9
|
+
* Cause: Reown/WalletConnect calls `parent.postMessage(event, '*')` from
|
|
10
|
+
* inside the widget iframe without transferring a MessagePort. The SDK's
|
|
11
|
+
* message listener passed `event.ports[0]` (undefined) to `setPort()`.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
interface TestValue {
|
|
15
|
+
id?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('EmbeddedComponent — portless postMessage (PAY-1056)', () => {
|
|
19
|
+
let container: HTMLDivElement;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
container = document.createElement('div');
|
|
23
|
+
document.body.appendChild(container);
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
test('should not crash when iframe sends a message without ports', () => {
|
|
27
|
+
const component = new EmbeddedComponent<TestValue, unknown>(
|
|
28
|
+
'about:blank?widget=true',
|
|
29
|
+
{},
|
|
30
|
+
);
|
|
31
|
+
component.embed(container);
|
|
32
|
+
|
|
33
|
+
const iframe = container.querySelector('iframe')!;
|
|
34
|
+
|
|
35
|
+
expect(() => {
|
|
36
|
+
window.dispatchEvent(
|
|
37
|
+
new MessageEvent('message', {
|
|
38
|
+
source: iframe.contentWindow,
|
|
39
|
+
data: { type: 'wc_sessionUpdate' },
|
|
40
|
+
ports: [],
|
|
41
|
+
}),
|
|
42
|
+
);
|
|
43
|
+
}).not.toThrow();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('should still accept the handshake message with a port', () => {
|
|
47
|
+
const component = new EmbeddedComponent<TestValue, unknown>(
|
|
48
|
+
'about:blank?widget=true',
|
|
49
|
+
{},
|
|
50
|
+
);
|
|
51
|
+
component.embed(container);
|
|
52
|
+
|
|
53
|
+
const iframe = container.querySelector('iframe')!;
|
|
54
|
+
const channel = new MessageChannel();
|
|
55
|
+
|
|
56
|
+
expect(() => {
|
|
57
|
+
window.dispatchEvent(
|
|
58
|
+
new MessageEvent('message', {
|
|
59
|
+
source: iframe.contentWindow,
|
|
60
|
+
data: {},
|
|
61
|
+
ports: [channel.port2],
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
}).not.toThrow();
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -12,6 +12,9 @@ interface TestOptions {
|
|
|
12
12
|
currency?: string;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
+
// Store callbacks for testing
|
|
16
|
+
const callbacks = new Map();
|
|
17
|
+
|
|
15
18
|
// Mock MessageEventManager
|
|
16
19
|
vi.mock('../utils/MessageEventManager', () => {
|
|
17
20
|
return {
|
|
@@ -30,9 +33,6 @@ vi.mock('../utils/MessageEventManager', () => {
|
|
|
30
33
|
};
|
|
31
34
|
});
|
|
32
35
|
|
|
33
|
-
// Store callbacks for testing
|
|
34
|
-
const callbacks = new Map();
|
|
35
|
-
|
|
36
36
|
describe('EmbeddedComponent', () => {
|
|
37
37
|
let mockParent: HTMLElement;
|
|
38
38
|
let mockAppendChild: ReturnType<typeof vi.fn>;
|
|
@@ -24,6 +24,7 @@ export default class EmbeddedComponent<V, O> {
|
|
|
24
24
|
private iframe?: HTMLIFrameElement;
|
|
25
25
|
private eventManager: MessageEventManager<V, O>;
|
|
26
26
|
private modal?: HTMLDialogElement;
|
|
27
|
+
private messageHandler?: (event: MessageEvent) => void;
|
|
27
28
|
|
|
28
29
|
/**
|
|
29
30
|
* Creates an instance of EmbeddedComponent.
|
|
@@ -112,18 +113,24 @@ export default class EmbeddedComponent<V, O> {
|
|
|
112
113
|
// this.iframe.style.backgroundColor = 'transparent';
|
|
113
114
|
parent.appendChild(this.iframe);
|
|
114
115
|
|
|
115
|
-
|
|
116
|
+
this.messageHandler = (event: MessageEvent) => {
|
|
116
117
|
if (event.source !== this.iframe?.contentWindow) {
|
|
117
118
|
return;
|
|
118
119
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
120
|
+
if (event.ports[0]) {
|
|
121
|
+
this.eventManager?.setPort(event.ports[0]);
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
window.addEventListener('message', this.messageHandler);
|
|
122
125
|
|
|
123
126
|
this.iframe?.contentWindow?.focus();
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
removeEmbed() {
|
|
130
|
+
if (this.messageHandler) {
|
|
131
|
+
window.removeEventListener('message', this.messageHandler);
|
|
132
|
+
this.messageHandler = undefined;
|
|
133
|
+
}
|
|
127
134
|
if (this.iframe) this.iframe.remove();
|
|
128
135
|
}
|
|
129
136
|
|
|
@@ -258,13 +265,15 @@ export default class EmbeddedComponent<V, O> {
|
|
|
258
265
|
'_blank',
|
|
259
266
|
'popup=true,width=600,height=600',
|
|
260
267
|
);
|
|
261
|
-
|
|
268
|
+
const popupMessageHandler = (event: MessageEvent) => {
|
|
262
269
|
if (event.source !== popup) {
|
|
263
270
|
return;
|
|
264
271
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
272
|
+
if (event.ports[0]) {
|
|
273
|
+
this.eventManager?.setPort(event.ports[0]);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
window.addEventListener('message', popupMessageHandler);
|
|
268
277
|
|
|
269
278
|
const removeCancel = this.on(CMType.CANCEL, () => {
|
|
270
279
|
popup?.close();
|
|
@@ -275,6 +284,7 @@ export default class EmbeddedComponent<V, O> {
|
|
|
275
284
|
});
|
|
276
285
|
|
|
277
286
|
return this.completion().finally(() => {
|
|
287
|
+
window.removeEventListener('message', popupMessageHandler);
|
|
278
288
|
removeCancel();
|
|
279
289
|
removeComplete();
|
|
280
290
|
});
|
package/src/notabene.ts
CHANGED
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
ComponentResponse,
|
|
15
15
|
ConnectionOptions,
|
|
16
16
|
ConnectionRequest,
|
|
17
|
+
ConsentConfig,
|
|
17
18
|
ContactSupportConfig,
|
|
18
19
|
CosmosMetadata,
|
|
19
20
|
Counterparty,
|
|
@@ -92,11 +93,9 @@ export {
|
|
|
92
93
|
componentResponseToIVMS101,
|
|
93
94
|
componentResponseToTxCreateRequest,
|
|
94
95
|
componentResponseToTxRequests,
|
|
95
|
-
componentResponseToV1TxCreateRequest,
|
|
96
96
|
} from './responseTransformer';
|
|
97
97
|
export type {
|
|
98
98
|
ResponseToTxRequestConfig,
|
|
99
|
-
TransactionCreateRequest,
|
|
100
99
|
TransactionCreateRequestV2,
|
|
101
100
|
TransactionIVMS101Request,
|
|
102
101
|
} from './responseTransformer';
|
|
@@ -143,6 +142,7 @@ export type {
|
|
|
143
142
|
ComponentResponse,
|
|
144
143
|
ConnectionOptions,
|
|
145
144
|
ConnectionRequest,
|
|
145
|
+
ConsentConfig,
|
|
146
146
|
ContactSupportConfig,
|
|
147
147
|
CosmosMetadata,
|
|
148
148
|
Counterparty,
|
|
@@ -58,26 +58,6 @@ const ivms101Body = componentResponseToIVMS101(response, delegateToken, {
|
|
|
58
58
|
});
|
|
59
59
|
```
|
|
60
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
61
|
## API
|
|
82
62
|
|
|
83
63
|
### `componentResponseToTxRequests(response, delegateToken, config?)`
|
|
@@ -127,16 +107,6 @@ Transforms a Notabene component response to IVMS101 format.
|
|
|
127
107
|
|
|
128
108
|
**Returns:** IVMS101 formatted request body
|
|
129
109
|
|
|
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
110
|
## Module Structure
|
|
141
111
|
|
|
142
112
|
```
|
|
@@ -333,7 +333,7 @@ describe('componentResponseToTxRequests', () => {
|
|
|
333
333
|
geographicAddress: [geographicAddress],
|
|
334
334
|
},
|
|
335
335
|
legalPerson: undefined,
|
|
336
|
-
accountNumber: [
|
|
336
|
+
accountNumber: [TEST_ADDRESS],
|
|
337
337
|
},
|
|
338
338
|
],
|
|
339
339
|
},
|
|
@@ -435,7 +435,7 @@ describe('componentResponseToTxRequests', () => {
|
|
|
435
435
|
},
|
|
436
436
|
},
|
|
437
437
|
legalPerson: undefined,
|
|
438
|
-
accountNumber: [
|
|
438
|
+
accountNumber: [TEST_ADDRESS],
|
|
439
439
|
},
|
|
440
440
|
],
|
|
441
441
|
},
|
|
@@ -9,13 +9,11 @@ export {
|
|
|
9
9
|
componentResponseToIVMS101,
|
|
10
10
|
componentResponseToTxCreateRequest,
|
|
11
11
|
componentResponseToTxRequests,
|
|
12
|
-
componentResponseToV1TxCreateRequest,
|
|
13
12
|
} from './transformer';
|
|
14
13
|
|
|
15
14
|
// Type exports
|
|
16
15
|
export {
|
|
17
16
|
type ResponseToTxRequestConfig,
|
|
18
|
-
type
|
|
19
|
-
type TransactionCreateRequestV2, // rename to TransactCreateRequest?
|
|
17
|
+
type TransactionCreateRequestV2,
|
|
20
18
|
type TransactionIVMS101Request,
|
|
21
19
|
} from './types';
|
|
@@ -1,11 +1,4 @@
|
|
|
1
1
|
import type { Agent, DID } from '@taprsvp/types';
|
|
2
|
-
import type {
|
|
3
|
-
Beneficiary,
|
|
4
|
-
NaturalPerson,
|
|
5
|
-
NaturalPersonName,
|
|
6
|
-
Originator,
|
|
7
|
-
Person,
|
|
8
|
-
} from '../ivms';
|
|
9
2
|
import {
|
|
10
3
|
PersonType,
|
|
11
4
|
type Deposit,
|
|
@@ -17,7 +10,6 @@ import type {
|
|
|
17
10
|
BaseRequestConfig,
|
|
18
11
|
ResponseToIVMS101RequestConfig,
|
|
19
12
|
ResponseToTxCreateRequestConfig,
|
|
20
|
-
TransactionCreateRequest,
|
|
21
13
|
TransactionCreateRequestV2,
|
|
22
14
|
TransactionIVMS101Request,
|
|
23
15
|
} from './types';
|
|
@@ -28,212 +20,6 @@ import {
|
|
|
28
20
|
isWithdrawal,
|
|
29
21
|
} from './utils';
|
|
30
22
|
|
|
31
|
-
// Constants
|
|
32
|
-
const DEFAULT_GEOGRAPHIC_ADDRESS = [
|
|
33
|
-
{
|
|
34
|
-
addressType: 'GEOG' as const,
|
|
35
|
-
addressLine: ['1234 Main Street'],
|
|
36
|
-
townName: 'Unknown',
|
|
37
|
-
country: 'US',
|
|
38
|
-
},
|
|
39
|
-
];
|
|
40
|
-
|
|
41
|
-
// Helper functions
|
|
42
|
-
function parseName(fullName: string) {
|
|
43
|
-
const trimmedName = fullName.trim();
|
|
44
|
-
if (!trimmedName) {
|
|
45
|
-
return { primaryIdentifier: '', secondaryIdentifier: '' };
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const parts = trimmedName.split(/\s+/);
|
|
49
|
-
const primaryIdentifier = parts[parts.length - 1];
|
|
50
|
-
const secondaryParts = parts.slice(0, -1);
|
|
51
|
-
const secondaryIdentifier =
|
|
52
|
-
secondaryParts.length > 0 ? secondaryParts.join(' ') : primaryIdentifier;
|
|
53
|
-
|
|
54
|
-
return { primaryIdentifier, secondaryIdentifier };
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
function copyIVMS(
|
|
58
|
-
type: 'originator' | 'beneficiary',
|
|
59
|
-
persons?: Person[],
|
|
60
|
-
accountNumber: string[] = ['1234567890'],
|
|
61
|
-
): Originator | Beneficiary {
|
|
62
|
-
const updatedPersons = persons?.map((person) => {
|
|
63
|
-
const updatedPerson = { ...person };
|
|
64
|
-
|
|
65
|
-
if (updatedPerson.naturalPerson) {
|
|
66
|
-
updatedPerson.naturalPerson = {
|
|
67
|
-
...updatedPerson.naturalPerson,
|
|
68
|
-
geographicAddress: DEFAULT_GEOGRAPHIC_ADDRESS,
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (updatedPerson.legalPerson) {
|
|
73
|
-
updatedPerson.legalPerson = {
|
|
74
|
-
...updatedPerson.legalPerson,
|
|
75
|
-
geographicAddress: DEFAULT_GEOGRAPHIC_ADDRESS,
|
|
76
|
-
};
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return updatedPerson;
|
|
80
|
-
});
|
|
81
|
-
|
|
82
|
-
return type === 'originator'
|
|
83
|
-
? { originatorPersons: updatedPersons, accountNumber }
|
|
84
|
-
: { beneficiaryPersons: updatedPersons, accountNumber };
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
function hostIVMS(
|
|
88
|
-
type: 'originator' | 'beneficiary',
|
|
89
|
-
name = 'John Doe',
|
|
90
|
-
): Originator | Beneficiary {
|
|
91
|
-
const { primaryIdentifier, secondaryIdentifier } = parseName(name);
|
|
92
|
-
|
|
93
|
-
const person: Person = {
|
|
94
|
-
naturalPerson: {
|
|
95
|
-
name: [
|
|
96
|
-
{
|
|
97
|
-
nameIdentifier: [
|
|
98
|
-
{
|
|
99
|
-
primaryIdentifier,
|
|
100
|
-
...(secondaryIdentifier ? { secondaryIdentifier } : {}),
|
|
101
|
-
nameIdentifierType: 'LEGL',
|
|
102
|
-
},
|
|
103
|
-
],
|
|
104
|
-
},
|
|
105
|
-
] as any,
|
|
106
|
-
geographicAddress: DEFAULT_GEOGRAPHIC_ADDRESS,
|
|
107
|
-
},
|
|
108
|
-
};
|
|
109
|
-
|
|
110
|
-
return type === 'originator'
|
|
111
|
-
? { originatorPersons: [person], accountNumber: ['1234567890'] }
|
|
112
|
-
: { beneficiaryPersons: [person], accountNumber: ['1234567890'] };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
const wrapNameInArray = (naturalPerson: NaturalPerson): NaturalPerson => ({
|
|
116
|
-
...naturalPerson,
|
|
117
|
-
// @ts-expect-error Preserving the original logic
|
|
118
|
-
name: naturalPerson.name
|
|
119
|
-
? ([naturalPerson.name] as NaturalPersonName[])
|
|
120
|
-
: undefined,
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
const transformPerson = (person: Person): Person => ({
|
|
124
|
-
...person,
|
|
125
|
-
naturalPerson: person.naturalPerson
|
|
126
|
-
? wrapNameInArray(person.naturalPerson)
|
|
127
|
-
: undefined,
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
const transformIVMS101 = (
|
|
131
|
-
{ beneficiary, originator }: IVMS101,
|
|
132
|
-
name?: string,
|
|
133
|
-
originatorEqualsBeneficiary?: boolean,
|
|
134
|
-
destination?: string,
|
|
135
|
-
operation?: 'withdrawal' | 'deposit',
|
|
136
|
-
) => {
|
|
137
|
-
if (operation === 'withdrawal') {
|
|
138
|
-
const transformedBeneficiary = beneficiary && {
|
|
139
|
-
...beneficiary,
|
|
140
|
-
...(destination && { accountNumber: [destination] }),
|
|
141
|
-
beneficiaryPersons: beneficiary.beneficiaryPersons?.map(transformPerson),
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
if (
|
|
145
|
-
originatorEqualsBeneficiary &&
|
|
146
|
-
transformedBeneficiary?.beneficiaryPersons
|
|
147
|
-
) {
|
|
148
|
-
const originator = copyIVMS(
|
|
149
|
-
'originator',
|
|
150
|
-
transformedBeneficiary?.beneficiaryPersons,
|
|
151
|
-
) as Originator;
|
|
152
|
-
|
|
153
|
-
return {
|
|
154
|
-
beneficiary: {
|
|
155
|
-
accountNumber: originator.accountNumber,
|
|
156
|
-
beneficiaryPersons: originator.originatorPersons,
|
|
157
|
-
},
|
|
158
|
-
originator,
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return {
|
|
163
|
-
beneficiary: transformedBeneficiary,
|
|
164
|
-
originator: hostIVMS('originator', name),
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
if (operation === 'deposit') {
|
|
169
|
-
const transformedOriginator = originator && {
|
|
170
|
-
...(destination && { accountNumber: [destination] }),
|
|
171
|
-
originatorPersons: originator.originatorPersons?.map(transformPerson),
|
|
172
|
-
};
|
|
173
|
-
|
|
174
|
-
if (
|
|
175
|
-
originatorEqualsBeneficiary &&
|
|
176
|
-
transformedOriginator?.originatorPersons
|
|
177
|
-
) {
|
|
178
|
-
const beneficiary = copyIVMS(
|
|
179
|
-
'beneficiary',
|
|
180
|
-
transformedOriginator?.originatorPersons,
|
|
181
|
-
) as Beneficiary;
|
|
182
|
-
|
|
183
|
-
return {
|
|
184
|
-
originator: {
|
|
185
|
-
accountNumber: beneficiary.accountNumber,
|
|
186
|
-
originatorPersons: beneficiary.beneficiaryPersons,
|
|
187
|
-
},
|
|
188
|
-
beneficiary,
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
return {
|
|
193
|
-
originator: transformedOriginator,
|
|
194
|
-
beneficiary: hostIVMS('beneficiary', name),
|
|
195
|
-
};
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
throw new Error('Invalid operation type');
|
|
199
|
-
};
|
|
200
|
-
|
|
201
|
-
export function mapToV1CreateRequest(
|
|
202
|
-
withdrawal: Withdrawal,
|
|
203
|
-
payload: V1Transaction,
|
|
204
|
-
ivms101: IVMS101,
|
|
205
|
-
): TransactionCreateRequest {
|
|
206
|
-
const { beneficiary, originator } = transformIVMS101(
|
|
207
|
-
ivms101,
|
|
208
|
-
withdrawal.customer?.name,
|
|
209
|
-
payload.originatorEqualsBeneficiary,
|
|
210
|
-
withdrawal.destination,
|
|
211
|
-
'withdrawal',
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
return {
|
|
215
|
-
transactionAsset: payload.transactionAsset,
|
|
216
|
-
transactionAmount: payload.transactionAmount,
|
|
217
|
-
beneficiaryDid: withdrawal.counterparty?.did,
|
|
218
|
-
originatorVASPdid: payload.originatorVASPdid,
|
|
219
|
-
...(payload.beneficiaryVASPdid && !payload.beneficiaryProof
|
|
220
|
-
? { beneficiaryVASPdid: payload.beneficiaryVASPdid }
|
|
221
|
-
: {}),
|
|
222
|
-
transactionBlockchainInfo: {
|
|
223
|
-
...(originator?.accountNumber && { origin: originator.accountNumber[0] }),
|
|
224
|
-
...(withdrawal.destination && { destination: withdrawal.destination }),
|
|
225
|
-
},
|
|
226
|
-
...(payload.beneficiaryProof && {
|
|
227
|
-
beneficiaryProof: payload.beneficiaryProof,
|
|
228
|
-
}),
|
|
229
|
-
...(beneficiary && { beneficiary }),
|
|
230
|
-
...(originator && { originator }),
|
|
231
|
-
...(payload.originatorEqualsBeneficiary && {
|
|
232
|
-
originatorEqualsBeneficiary: payload.originatorEqualsBeneficiary,
|
|
233
|
-
}),
|
|
234
|
-
};
|
|
235
|
-
}
|
|
236
|
-
|
|
237
23
|
export function mapToTransactCreateRequest(
|
|
238
24
|
transaction: Withdrawal | Deposit,
|
|
239
25
|
payload: V1Transaction,
|
|
@@ -311,7 +97,9 @@ export function mapToAppendPiiRequest(
|
|
|
311
97
|
config: ResponseToIVMS101RequestConfig & Required<BaseRequestConfig>,
|
|
312
98
|
): TransactionIVMS101Request {
|
|
313
99
|
if (isWithdrawal(transaction)) {
|
|
314
|
-
|
|
100
|
+
// For outgoing transfers, the beneficiary is the counterparty — use the
|
|
101
|
+
// destination blockchain address as their accountNumber.
|
|
102
|
+
const beneficiaryAccountNumber = transaction.destination;
|
|
315
103
|
|
|
316
104
|
const beneficiaryPersons =
|
|
317
105
|
// If counterparty type is SELF, reuse originator data for beneficiary
|
|
@@ -319,7 +107,7 @@ export function mapToAppendPiiRequest(
|
|
|
319
107
|
? config.originator.originatorPerson
|
|
320
108
|
: // Convert all beneficiary persons from V1 to V2 format
|
|
321
109
|
ivms101.beneficiary?.beneficiaryPersons?.map((person) =>
|
|
322
|
-
convertPersonToV2(person,
|
|
110
|
+
convertPersonToV2(person, beneficiaryAccountNumber),
|
|
323
111
|
) || [];
|
|
324
112
|
|
|
325
113
|
return {
|
|
@@ -332,14 +120,17 @@ export function mapToAppendPiiRequest(
|
|
|
332
120
|
};
|
|
333
121
|
}
|
|
334
122
|
|
|
335
|
-
|
|
123
|
+
// For incoming transfers, the originator is the counterparty — use the
|
|
124
|
+
// source blockchain address as their accountNumber.
|
|
125
|
+
const source = (transaction as Deposit).source;
|
|
126
|
+
const originatorAccountNumber = Array.isArray(source) ? source[0] : source;
|
|
336
127
|
|
|
337
128
|
const originatorPersons =
|
|
338
129
|
transaction.counterparty?.type === PersonType.SELF && config.beneficiary
|
|
339
130
|
? config.beneficiary.beneficiaryPerson
|
|
340
131
|
: // Convert all originator persons from V1 to V2 format
|
|
341
132
|
ivms101.originator?.originatorPersons?.map((person) =>
|
|
342
|
-
convertPersonToV2(person,
|
|
133
|
+
convertPersonToV2(person, originatorAccountNumber),
|
|
343
134
|
) || [];
|
|
344
135
|
|
|
345
136
|
return {
|
|
@@ -8,18 +8,13 @@ import {
|
|
|
8
8
|
type TransactionResponse,
|
|
9
9
|
type Withdrawal,
|
|
10
10
|
} from '../types';
|
|
11
|
-
import {
|
|
12
|
-
mapToAppendPiiRequest,
|
|
13
|
-
mapToTransactCreateRequest,
|
|
14
|
-
mapToV1CreateRequest,
|
|
15
|
-
} from './mappers';
|
|
11
|
+
import { mapToAppendPiiRequest, mapToTransactCreateRequest } from './mappers';
|
|
16
12
|
import type {
|
|
17
13
|
BaseRequestConfig,
|
|
18
14
|
DelegateToken,
|
|
19
15
|
ResponseToIVMS101RequestConfig,
|
|
20
16
|
ResponseToTxCreateRequestConfig,
|
|
21
17
|
ResponseToTxRequestConfig,
|
|
22
|
-
TransactionCreateRequest,
|
|
23
18
|
TransactionCreateRequestV2,
|
|
24
19
|
TransactionIVMS101Request,
|
|
25
20
|
} from './types';
|
|
@@ -147,57 +142,6 @@ export function enrichConfig(
|
|
|
147
142
|
Required<BaseRequestConfig>;
|
|
148
143
|
}
|
|
149
144
|
|
|
150
|
-
/**
|
|
151
|
-
* Transforms a Notabene component response to a Version 1 API request body
|
|
152
|
-
*
|
|
153
|
-
* @param response - The response from the Notabene TX Create component
|
|
154
|
-
* @returns The transformed request body ready for the Version 1 API
|
|
155
|
-
*
|
|
156
|
-
* @example
|
|
157
|
-
* ```typescript
|
|
158
|
-
* import { componentResponseToV1TxCreateRequest } from '$lib/notabene-tx-transformer';
|
|
159
|
-
*
|
|
160
|
-
* withdrawal.on('complete', async (result) => {
|
|
161
|
-
* const requestBody = componentResponseToV1TxCreateRequest(result.response);
|
|
162
|
-
*
|
|
163
|
-
* await fetch(endpointUrl, {
|
|
164
|
-
* method: 'POST',
|
|
165
|
-
* body: JSON.stringify(requestBody)
|
|
166
|
-
* });
|
|
167
|
-
* });
|
|
168
|
-
* ```
|
|
169
|
-
*/
|
|
170
|
-
export function componentResponseToV1TxCreateRequest(
|
|
171
|
-
response: TransactionResponse<Withdrawal>,
|
|
172
|
-
): TransactionCreateRequest {
|
|
173
|
-
if (!response.txCreate || !response.ivms101) {
|
|
174
|
-
throw new Error(
|
|
175
|
-
'Invalid response: missing required txCreate or ivms101 data',
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const { value, ivms101, proof, txCreate } = response;
|
|
180
|
-
|
|
181
|
-
// Build withdrawal object from response data for V1 API
|
|
182
|
-
const withdrawal: Withdrawal = {
|
|
183
|
-
destination: value?.destination || '',
|
|
184
|
-
counterparty: value?.counterparty || {},
|
|
185
|
-
agent: value?.agent,
|
|
186
|
-
account: value?.account,
|
|
187
|
-
proof,
|
|
188
|
-
asset:
|
|
189
|
-
value?.asset ||
|
|
190
|
-
(typeof txCreate.transactionAsset === 'string'
|
|
191
|
-
? txCreate.transactionAsset
|
|
192
|
-
: txCreate.transactionAsset?.caip19) ||
|
|
193
|
-
'',
|
|
194
|
-
amountDecimal: value?.amountDecimal ?? txCreate.transactionAmount ?? 0,
|
|
195
|
-
customer: value?.customer,
|
|
196
|
-
} as Withdrawal;
|
|
197
|
-
|
|
198
|
-
return mapToV1CreateRequest(withdrawal, txCreate, ivms101 as any);
|
|
199
|
-
}
|
|
200
|
-
|
|
201
145
|
/**
|
|
202
146
|
* Transforms a Notabene component response into txCreate, IVMS101, and confirmRelationship request bodies
|
|
203
147
|
*
|
|
@@ -32,24 +32,6 @@ export interface ResponseToIVMS101RequestConfig extends BaseRequestConfig {
|
|
|
32
32
|
export type ResponseToTxRequestConfig = ResponseToTxCreateRequestConfig &
|
|
33
33
|
ResponseToIVMS101RequestConfig;
|
|
34
34
|
|
|
35
|
-
export interface TransactionCreateRequest {
|
|
36
|
-
transactionAsset: any;
|
|
37
|
-
transactionAmount: string;
|
|
38
|
-
beneficiaryDid?: string;
|
|
39
|
-
originatorVASPdid: string;
|
|
40
|
-
beneficiaryVASPdid?: string;
|
|
41
|
-
beneficiaryVASPname?: string;
|
|
42
|
-
beneficiaryVASPwebsite?: string;
|
|
43
|
-
transactionBlockchainInfo: {
|
|
44
|
-
origin?: string;
|
|
45
|
-
destination?: string;
|
|
46
|
-
};
|
|
47
|
-
beneficiaryProof?: any;
|
|
48
|
-
beneficiary?: any;
|
|
49
|
-
originator?: any;
|
|
50
|
-
originatorEqualsBeneficiary?: boolean;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
35
|
export interface TransactionCreateRequestV2 {
|
|
54
36
|
originator: {
|
|
55
37
|
'@id': string;
|