@monerium/sdk 3.5.0 → 4.0.0
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 +341 -367
- package/dist/index.d.ts +804 -904
- package/dist/index.js +3 -4
- package/dist/index.mjs +3 -4
- package/package.json +3 -4
package/README.md
CHANGED
|
@@ -5,27 +5,27 @@
|
|
|
5
5
|
|
|
6
6
|
<a href="https://docs.monerium.com">
|
|
7
7
|
<picture>
|
|
8
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/Developer_portal-2c6ca7"
|
|
9
|
-
<img src="https://img.shields.io/badge/Developer_portal-2c6ca7" alt="Static Badge"
|
|
8
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/Developer_portal-2c6ca7"/>
|
|
9
|
+
<img src="https://img.shields.io/badge/Developer_portal-2c6ca7" alt="Static Badge"/>
|
|
10
10
|
</picture>
|
|
11
11
|
</a>
|
|
12
12
|
<a href="https://docs.monerium.com/api">
|
|
13
13
|
<picture>
|
|
14
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/API_documentation-2c6ca7"
|
|
15
|
-
<img src="https://img.shields.io/badge/API_documentation-2c6ca7" alt="Static Badge"
|
|
14
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/badge/API_documentation-2c6ca7"/>
|
|
15
|
+
<img src="https://img.shields.io/badge/API_documentation-2c6ca7" alt="Static Badge"/>
|
|
16
16
|
</picture>
|
|
17
17
|
</a>
|
|
18
|
-
<br
|
|
18
|
+
<br/><br/>
|
|
19
19
|
<a href="https://www.npmjs.com/package/@monerium/sdk">
|
|
20
20
|
<picture>
|
|
21
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/v/%40monerium%2Fsdk?colorA=2c6ca7&colorB=21262d"
|
|
22
|
-
<img src="https://img.shields.io/npm/v/%40monerium%2Fsdk?colorA=f6f8fa&colorB=f6f8fa" alt="Version"
|
|
21
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/npm/v/%40monerium%2Fsdk?colorA=2c6ca7&colorB=21262d"/>
|
|
22
|
+
<img src="https://img.shields.io/npm/v/%40monerium%2Fsdk?colorA=f6f8fa&colorB=f6f8fa" alt="Version"/>
|
|
23
23
|
</picture>
|
|
24
24
|
</a>
|
|
25
25
|
<a href="https://github.com/monerium/js-monorepo/issues">
|
|
26
26
|
<picture>
|
|
27
|
-
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/issues/monerium/js-monorepo?colorA=2c6ca7&colorB=21262d"
|
|
28
|
-
<img src="https://img.shields.io/github/issues/monerium/js-monorepo?colorA=2c6ca7&colorB=21262d" alt="Version"
|
|
27
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://img.shields.io/github/issues/monerium/js-monorepo?colorA=2c6ca7&colorB=21262d"/>
|
|
28
|
+
<img src="https://img.shields.io/github/issues/monerium/js-monorepo?colorA=2c6ca7&colorB=21262d" alt="Version"/>
|
|
29
29
|
</picture>
|
|
30
30
|
</a>
|
|
31
31
|
|
|
@@ -36,408 +36,396 @@ All incoming euro payments are automatically minted as EURe tokens to your walle
|
|
|
36
36
|
Sending EURe to traditional bank accounts is just as easy.
|
|
37
37
|
With a single signature from your wallet, your EURe is burned and sent as Euros to any bank account.
|
|
38
38
|
|
|
39
|
-
## Documentation
|
|
40
|
-
|
|
41
|
-
- [Documentation](../../apps/developer/docs/packages/SDK/index.md)
|
|
42
|
-
- [Documentation - MoneriumClient](../../apps/developer/docs/packages/SDK/classes/MoneriumClient.md)
|
|
43
|
-
|
|
44
|
-
## Table of Contents
|
|
45
|
-
|
|
46
|
-
- [Installation](#installation)
|
|
47
|
-
- [Configuration](#configuration)
|
|
48
|
-
- [Usage Examples](#usage-examples)
|
|
49
|
-
- [API Reference](#api-reference)
|
|
50
|
-
- [Contributing](#contributing)
|
|
51
|
-
- [FAQs](#faqs)
|
|
52
|
-
- [Support](#support)
|
|
53
|
-
- [Release Notes](#release-notes)
|
|
54
|
-
- [License](#license)
|
|
55
|
-
|
|
56
39
|
## Installation
|
|
57
40
|
|
|
58
|
-
### Prerequisites
|
|
59
|
-
|
|
60
|
-
Node v16.15 or higher is required.
|
|
61
|
-
|
|
62
41
|
```sh
|
|
63
|
-
|
|
42
|
+
pnpm add @monerium/sdk
|
|
64
43
|
```
|
|
65
44
|
|
|
66
|
-
##
|
|
67
|
-
|
|
68
|
-
### Environments - URLs
|
|
69
|
-
|
|
70
|
-
| Environment | Web | API |
|
|
71
|
-
| ----------- | ---------------------------- | ------------------------ |
|
|
72
|
-
| sandbox | https://sandbox.monerium.app | https://api.monerium.dev |
|
|
73
|
-
| production | https://monerium.app | https://api.monerium.app |
|
|
45
|
+
## Usage Patterns
|
|
74
46
|
|
|
75
|
-
|
|
47
|
+
This section demonstrates how to use the plan-specific client classes (`MoneriumPrivateClient`, `MoneriumWhitelabelClient`, and `MoneriumOAuthClient`) for the integration flows.
|
|
76
48
|
|
|
77
|
-
|
|
78
|
-
| ----------- | -------- | -------- |
|
|
79
|
-
| sandbox | ethereum | sepolia |
|
|
80
|
-
| | polygon | mumbai |
|
|
81
|
-
| | gnosis | chiado |
|
|
82
|
-
| | linea | sepolia |
|
|
83
|
-
| | scroll | sepolia |
|
|
84
|
-
| | camino | columbus |
|
|
85
|
-
| production | ethereum | mainnet |
|
|
86
|
-
| | polygon | mainnet |
|
|
87
|
-
| | gnosis | mainnet |
|
|
88
|
-
| | linea | mainnet |
|
|
89
|
-
| | scroll | mainnet |
|
|
90
|
-
| | camino | mainnet |
|
|
49
|
+
### 1. Private Plan
|
|
91
50
|
|
|
92
|
-
|
|
51
|
+
[Read the Private Integration Guide](https://docs.monerium.com/private)
|
|
93
52
|
|
|
94
|
-
|
|
53
|
+
The Private plan gives you direct API access to your own Monerium account.
|
|
54
|
+
You use `MoneriumPrivateClient` for Server-to-Server communication.
|
|
95
55
|
|
|
96
|
-
|
|
56
|
+
Instead of manually passing static access tokens, the recommended approach is to provide a `getAccessToken` callback. This allows the client to dynamically retrieve, cache, and refresh tokens seamlessly across all requests using a single global client instance.
|
|
97
57
|
|
|
98
|
-
|
|
58
|
+
```typescript
|
|
59
|
+
import { MoneriumPrivateClient, Currency } from '@monerium/sdk';
|
|
99
60
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
import { MoneriumClient } from '@monerium/sdk';
|
|
103
|
-
// Initialize the client with credentials
|
|
104
|
-
const monerium = new MoneriumClient({
|
|
105
|
-
environment: 'sandbox',
|
|
106
|
-
clientId: 'your_client_credentials_uuid', // replace with your client ID
|
|
107
|
-
clientSecret: 'your_client_secret', // replace with your client secret
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
await monerium.getAccess();
|
|
111
|
-
|
|
112
|
-
// Retrieve profiles the client has access to.
|
|
113
|
-
await monerium.getProfiles();
|
|
114
|
-
|
|
115
|
-
// Access tokens are now available for use.
|
|
116
|
-
const { access_token, refresh_token } = monerium.bearerProfile as BearerProfile;
|
|
117
|
-
|
|
118
|
-
// Use refresh token to get a new access token
|
|
119
|
-
await monerium.getAccess(refresh_token);
|
|
120
|
-
|
|
121
|
-
/*
|
|
122
|
-
* Upcoming v4 - factory function
|
|
123
|
-
*/
|
|
124
|
-
|
|
125
|
-
import {
|
|
126
|
-
randomPKCECodeVerifier,
|
|
127
|
-
calculatePKCECodeChallenge,
|
|
128
|
-
buildAuthorizationUrl,
|
|
129
|
-
authorizationCodeGrant,
|
|
130
|
-
refreshTokenGrant,
|
|
131
|
-
createMoneriumClient,
|
|
132
|
-
} from '@monerium/sdk';
|
|
133
|
-
|
|
134
|
-
// --- Initiate login ---
|
|
135
|
-
const codeVerifier = randomPKCECodeVerifier();
|
|
136
|
-
const codeChallenge = calculatePKCECodeChallenge(codeVerifier);
|
|
137
|
-
session.set('pkce_verifier', codeVerifier); // server-side session
|
|
138
|
-
|
|
139
|
-
const url = buildAuthorizationUrl({
|
|
140
|
-
environment: 'sandbox',
|
|
141
|
-
clientId: 'your-client-id',
|
|
142
|
-
redirectUri: 'https://your-app.com/callback',
|
|
143
|
-
codeChallenge,
|
|
144
|
-
});
|
|
145
|
-
res.redirect(url);
|
|
146
|
-
|
|
147
|
-
// --- On the callback page ---
|
|
148
|
-
const { code } = parseAuthorizationResponse(
|
|
149
|
-
new URL(req.url, 'https://your-app.com')
|
|
150
|
-
);
|
|
151
|
-
const codeVerifier = session.get('pkce_verifier');
|
|
152
|
-
session.delete('pkce_verifier');
|
|
153
|
-
|
|
154
|
-
const bearerProfile = await authorizationCodeGrant({
|
|
155
|
-
environment: 'sandbox',
|
|
156
|
-
clientId: 'your-client-id',
|
|
157
|
-
redirectUri: 'https://your-app.com/callback',
|
|
158
|
-
code,
|
|
159
|
-
codeVerifier,
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
req.session.accessToken = bearerProfile.access_token;
|
|
163
|
-
req.session.refreshToken = bearerProfile.refresh_token;
|
|
164
|
-
req.session.accessExpiry = Date.now() + bearerProfile.expires_in * 1000;
|
|
165
|
-
|
|
166
|
-
// --- Use the API ---
|
|
167
|
-
const client = createMoneriumClient({
|
|
61
|
+
// 1. Initialize the client ONCE globally
|
|
62
|
+
const client: MoneriumPrivateClient = new MoneriumPrivateClient({
|
|
168
63
|
environment: 'sandbox',
|
|
169
64
|
getAccessToken: async () => {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
session.accessExpiry = Date.now() + newProfile.expires_in * 1000;
|
|
178
|
-
return newProfile.access_token;
|
|
65
|
+
// a. Check in-memory cache
|
|
66
|
+
let token = memoryCache.get('monerium_token');
|
|
67
|
+
|
|
68
|
+
// b. Fallback to Database
|
|
69
|
+
if (!token) {
|
|
70
|
+
token = await db.getToken();
|
|
71
|
+
if (token) memoryCache.set('monerium_token', token);
|
|
179
72
|
}
|
|
180
|
-
return session.accessToken;
|
|
181
|
-
},
|
|
182
|
-
});
|
|
183
|
-
|
|
184
|
-
const profiles = await client.getProfiles();
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
API documentation:
|
|
188
|
-
|
|
189
|
-
- [/auth/token](https://docs.monerium.com/api#operation/auth-token)
|
|
190
|
-
|
|
191
|
-
#### Initialize and authenticate using Authorization Code Flow with PKCE
|
|
192
73
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
74
|
+
// c. If token is missing or expired, fetch a new one
|
|
75
|
+
if (!token || isExpired(token)) {
|
|
76
|
+
// clientCredentialsGrant does not require authentication
|
|
77
|
+
const auth = await client.clientCredentialsGrant('YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET');
|
|
78
|
+
token = auth.access_token;
|
|
79
|
+
|
|
80
|
+
memoryCache.set('monerium_token', token);
|
|
81
|
+
await db.saveToken(token);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return token;
|
|
85
|
+
}
|
|
86
|
+
});
|
|
196
87
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
environment: 'sandbox',
|
|
208
|
-
clientId: 'f99e629b-6dca-11ee-8aa6-5273f65ed05b',
|
|
209
|
-
redirectUri: 'http://localhost:4200',
|
|
88
|
+
// 2. Set up Webhooks (One-off initialization)
|
|
89
|
+
async function setupWebhooks() {
|
|
90
|
+
const { subscriptions } = await client.getSubscriptions();
|
|
91
|
+
const webhookUrl = 'https://your-app.com/webhooks/monerium';
|
|
92
|
+
|
|
93
|
+
if (!subscriptions.some(sub => sub.url === webhookUrl)) {
|
|
94
|
+
await client.createSubscription({
|
|
95
|
+
url: webhookUrl,
|
|
96
|
+
secret: 'whsec_YOUR_BASE64_ENCODED_SECRET',
|
|
97
|
+
types: ['order.created', 'order.updated']
|
|
210
98
|
});
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
useEffect(() => {
|
|
215
|
-
const connect = async () => {
|
|
216
|
-
if (monerium) {
|
|
217
|
-
setIsAuthorized(await monerium.getAccess());
|
|
218
|
-
}
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
connect();
|
|
99
|
+
}
|
|
100
|
+
}
|
|
222
101
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
102
|
+
async function runPrivateFlow() {
|
|
103
|
+
// 3. Link a Wallet
|
|
104
|
+
const { addresses } = await client.getAddresses();
|
|
105
|
+
if (!addresses.some(a => a.address === '0xYourAddress...')) {
|
|
106
|
+
await client.linkAddress({
|
|
107
|
+
address: '0xYourAddress...',
|
|
108
|
+
message: 'I hereby declare that I am the address owner.',
|
|
109
|
+
signature: '0xYourSignature...',
|
|
110
|
+
chain: 'ethereum'
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// 4. Send payments (Place an Order)
|
|
115
|
+
// Check if profile and IBAN are approved before placing an order
|
|
116
|
+
const profiles = await client.getProfiles();
|
|
117
|
+
const defaultProfile = profiles.profiles[0];
|
|
118
|
+
const { ibans } = await client.getIbans();
|
|
119
|
+
const activeIban = ibans.find(i => i.state === 'approved');
|
|
120
|
+
|
|
121
|
+
if (defaultProfile?.state === 'approved' && activeIban) {
|
|
122
|
+
await client.placeOrder({
|
|
123
|
+
address: '0xYourAddress...',
|
|
124
|
+
chain: 'ethereum',
|
|
125
|
+
amount: '100.00',
|
|
126
|
+
signature: '0xYourSignature...',
|
|
127
|
+
currency: Currency.eur,
|
|
128
|
+
counterpart: {
|
|
129
|
+
identifier: {
|
|
130
|
+
standard: 'iban',
|
|
131
|
+
iban: 'YOUR_IBAN',
|
|
132
|
+
bic: 'YOUR_BIC'
|
|
133
|
+
},
|
|
134
|
+
details: {
|
|
135
|
+
firstName: 'John',
|
|
136
|
+
lastName: 'Doe',
|
|
137
|
+
country: 'IS'
|
|
238
138
|
}
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
return (
|
|
245
|
-
<div>
|
|
246
|
-
{!isAuthorized && <button onClick={() => monerium?.authorize()}>Connect</button>}
|
|
247
|
-
|
|
248
|
-
<p>{profiles[0]?.name}</p>
|
|
249
|
-
</div>
|
|
250
|
-
);
|
|
139
|
+
},
|
|
140
|
+
message: 'Payment for services'
|
|
141
|
+
});
|
|
142
|
+
}
|
|
251
143
|
}
|
|
252
|
-
```
|
|
253
|
-
|
|
254
|
-
API documentation:
|
|
255
|
-
|
|
256
|
-
- [/auth](https://docs.monerium.com/api#operation/auth)
|
|
257
|
-
- [/auth/token](https://docs.monerium.com/api#operation/auth-token)
|
|
258
|
-
|
|
259
|
-
#### Get account information
|
|
260
|
-
|
|
261
|
-
```ts
|
|
262
|
-
// Get all profiles
|
|
263
|
-
const { profiles }: Profile[] = await monerium.getProfiles();
|
|
264
144
|
|
|
265
|
-
//
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
)
|
|
145
|
+
// 5. Handle Webhooks (e.g., in your Express route)
|
|
146
|
+
async function handleMoneriumWebhook(event: any) {
|
|
147
|
+
// Automatically provision IBAN when the profile is approved
|
|
148
|
+
if (event.type === 'profile.updated' && event.meta?.state === 'approved') {
|
|
149
|
+
const { ibans } = await client.getIbans();
|
|
150
|
+
|
|
151
|
+
if (ibans.length > 0) {
|
|
152
|
+
if (!ibans.some(i => i.address === '0xYourAddress...' && i.chain === 'ethereum')) {
|
|
153
|
+
await client.moveIban({
|
|
154
|
+
iban: ibans[0].iban,
|
|
155
|
+
address: '0xYourAddress...',
|
|
156
|
+
chain: 'ethereum'
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
} else {
|
|
160
|
+
await client.requestIban({
|
|
161
|
+
address: '0xYourAddress...',
|
|
162
|
+
chain: 'ethereum'
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
}
|
|
269
166
|
|
|
270
|
-
|
|
271
|
-
|
|
167
|
+
if (event.type === 'order.updated') {
|
|
168
|
+
console.log('Order status changed:', event.meta?.state);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
272
171
|
```
|
|
273
172
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
- [/profile](https://docs.monerium.com/api#operation/profile)
|
|
277
|
-
- [/profile/{profileId{/balances](https://docs.monerium.com/api#operation/profile-balances)
|
|
278
|
-
|
|
279
|
-
#### Get token information
|
|
280
|
-
|
|
281
|
-
Get the contract addresses of EURe tokens.
|
|
173
|
+
### 2. Whitelabel Plan
|
|
282
174
|
|
|
283
|
-
|
|
284
|
-
const tokens: Token[] = await monerium.getTokens();
|
|
285
|
-
```
|
|
175
|
+
[Read the Whitelabel Integration Guide](https://docs.monerium.com/whitelabel)
|
|
286
176
|
|
|
287
|
-
|
|
177
|
+
Embed regulated euro accounts and SEPA payments in your product under your own brand.
|
|
178
|
+
You use `MoneriumWhitelabelClient` to manage profiles on behalf of your customers.
|
|
288
179
|
|
|
289
|
-
-
|
|
180
|
+
Similar to the Private plan, use the `getAccessToken` pattern for efficient, secure server-to-server authentication.
|
|
290
181
|
|
|
291
|
-
|
|
182
|
+
```typescript
|
|
183
|
+
import { MoneriumWhitelabelClient } from '@monerium/sdk';
|
|
292
184
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
185
|
+
// 1. Initialize the client ONCE globally
|
|
186
|
+
const client: MoneriumWhitelabelClient = new MoneriumWhitelabelClient({
|
|
187
|
+
environment: 'sandbox',
|
|
188
|
+
getAccessToken: async () => {
|
|
189
|
+
let token = memoryCache.get('monerium_token');
|
|
190
|
+
|
|
191
|
+
if (!token) {
|
|
192
|
+
token = await db.getToken();
|
|
193
|
+
if (token) memoryCache.set('monerium_token', token);
|
|
194
|
+
}
|
|
297
195
|
|
|
298
|
-
|
|
196
|
+
if (!token || isExpired(token)) {
|
|
197
|
+
const auth = await client.clientCredentialsGrant('YOUR_CLIENT_ID', 'YOUR_CLIENT_SECRET');
|
|
198
|
+
token = auth.access_token;
|
|
199
|
+
|
|
200
|
+
memoryCache.set('monerium_token', token);
|
|
201
|
+
await db.saveToken(token);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return token;
|
|
205
|
+
}
|
|
206
|
+
});
|
|
299
207
|
|
|
300
|
-
|
|
301
|
-
|
|
208
|
+
// 2. Set up Webhooks (One-off initialization, listen for profile approvals)
|
|
209
|
+
async function setupWebhooks() {
|
|
210
|
+
const { subscriptions } = await client.getSubscriptions();
|
|
211
|
+
const webhookUrl = 'https://your-app.com/webhooks/monerium';
|
|
212
|
+
|
|
213
|
+
if (!subscriptions.some(sub => sub.url === webhookUrl)) {
|
|
214
|
+
await client.createSubscription({
|
|
215
|
+
url: webhookUrl,
|
|
216
|
+
secret: 'whsec_YOUR_BASE64_ENCODED_SECRET',
|
|
217
|
+
types: ['profile.updated', 'iban.updated', 'order.created', 'order.updated']
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
}
|
|
302
221
|
|
|
303
|
-
|
|
222
|
+
async function runWhitelabelFlow() {
|
|
223
|
+
// 3. Onboard the customer (Create a Profile)
|
|
224
|
+
const profile = await client.createProfile({
|
|
225
|
+
kind: 'personal'
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
// 4. Submit identity data (e.g., via Sumsub integration)
|
|
229
|
+
// await client.shareProfileKYC({ ... });
|
|
230
|
+
|
|
231
|
+
// 5. Link a Wallet
|
|
232
|
+
const { addresses } = await client.getAddresses({ profile: profile.id });
|
|
233
|
+
if (!addresses.some(a => a.address === '0xCustomerAddress...')) {
|
|
234
|
+
await client.linkAddress({
|
|
235
|
+
profile: profile.id,
|
|
236
|
+
address: '0xCustomerAddress...',
|
|
237
|
+
message: 'I hereby declare that I am the address owner.',
|
|
238
|
+
signature: '0xCustomerSignature...',
|
|
239
|
+
chain: 'polygon'
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// 6. Send/Receive Payments
|
|
244
|
+
// Check if profile and IBAN are approved before interacting
|
|
245
|
+
const currentProfile = await client.getProfile(profile.id);
|
|
246
|
+
const { ibans } = await client.getIbans({ profile: profile.id });
|
|
247
|
+
const activeIban = ibans.find(i => i.state === 'approved');
|
|
248
|
+
|
|
249
|
+
if (currentProfile.state === 'approved' && activeIban) {
|
|
250
|
+
// Monerium handles incoming SEPA payments automatically once the IBAN is active.
|
|
251
|
+
// You can also place outgoing orders here using client.placeOrder()
|
|
252
|
+
}
|
|
253
|
+
}
|
|
304
254
|
|
|
305
|
-
//
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
255
|
+
// 7. Handle Webhooks (e.g., in your Express route)
|
|
256
|
+
async function handleMoneriumWebhook(event: any) {
|
|
257
|
+
// Automatically provision IBAN when the profile is approved
|
|
258
|
+
if (event.type === 'profile.updated' && event.meta?.state === 'approved') {
|
|
259
|
+
const profileId = event.meta?.profile;
|
|
260
|
+
const { ibans } = await client.getIbans({ profile: profileId });
|
|
261
|
+
|
|
262
|
+
if (ibans.length > 0) {
|
|
263
|
+
if (!ibans.some(i => i.address === '0xCustomerAddress...' && i.chain === 'polygon')) {
|
|
264
|
+
await client.moveIban({
|
|
265
|
+
iban: ibans[0].iban,
|
|
266
|
+
address: '0xCustomerAddress...',
|
|
267
|
+
chain: 'polygon'
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
await client.requestIban({
|
|
272
|
+
address: '0xCustomerAddress...',
|
|
273
|
+
chain: 'polygon'
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
309
277
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
message: LINK_MESSAGE
|
|
315
|
-
signature,
|
|
316
|
-
chain: 'ethereum',
|
|
317
|
-
} as LinkAddress);
|
|
278
|
+
if (event.type === 'iban.updated') {
|
|
279
|
+
console.log('IBAN state changed for profile:', event.meta?.profile);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
318
282
|
```
|
|
319
283
|
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
- [/profile/{profileId{/addresses](https://docs.monerium.com/api#operation/profile-addresses)
|
|
284
|
+
### 3. OAuth Plan
|
|
323
285
|
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
```ts
|
|
327
|
-
// Get orders for a specific profile
|
|
328
|
-
const orders: Order[] = await monerium.getOrders(profileId);
|
|
329
|
-
```
|
|
286
|
+
[Read the OAuth Integration Guide](https://docs.monerium.com/oauth)
|
|
330
287
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
import { placeOrderMessage } from '@monerium/sdk';
|
|
334
|
-
import { walletClient } from '...'; // See Viem documentation
|
|
288
|
+
The OAuth flow lets users authorize your application to access their Monerium account.
|
|
289
|
+
You use `MoneriumOAuthClient` as it is safe for both browser and server environments (it does not contain `clientCredentialsGrant`).
|
|
335
290
|
|
|
336
|
-
|
|
337
|
-
const iban = 'EE12341234123412341234'; // replace with requested IBAN
|
|
291
|
+
When managing multiple users, you can also use `getAccessToken` to fetch the specific user's token from your database and refresh it automatically via `refreshTokenGrant` if it expires.
|
|
338
292
|
|
|
339
|
-
|
|
340
|
-
|
|
293
|
+
```typescript
|
|
294
|
+
import { MoneriumOAuthClient, generatePKCE } from '@monerium/sdk';
|
|
341
295
|
|
|
342
|
-
//
|
|
343
|
-
|
|
296
|
+
// 1. Initialize the client (Browser or Server)
|
|
297
|
+
const client: MoneriumOAuthClient = new MoneriumOAuthClient({
|
|
298
|
+
environment: 'sandbox',
|
|
299
|
+
getAccessToken: async () => {
|
|
300
|
+
// Fetch the authenticated user's access token from your database/session
|
|
301
|
+
let { accessToken, refreshToken, expiresAt } = await db.getUserTokens(userId);
|
|
302
|
+
|
|
303
|
+
// If expired, refresh it automatically using the OAuth client
|
|
304
|
+
if (Date.now() >= expiresAt) {
|
|
305
|
+
const auth = await client.refreshTokenGrant({
|
|
306
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
307
|
+
refreshToken: refreshToken
|
|
308
|
+
});
|
|
344
309
|
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
});
|
|
310
|
+
accessToken = auth.access_token;
|
|
311
|
+
await db.saveUserTokens(userId, auth);
|
|
312
|
+
}
|
|
349
313
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
amount,
|
|
353
|
-
signature,
|
|
354
|
-
currency: 'eur',
|
|
355
|
-
address: '0xUserAddress72413Fa92980B889A1eCE84dD', // user wallet address
|
|
356
|
-
counterpart: {
|
|
357
|
-
identifier: {
|
|
358
|
-
standard: 'iban', // PaymentStandard.iban,
|
|
359
|
-
iban,
|
|
360
|
-
},
|
|
361
|
-
details: {
|
|
362
|
-
firstName: 'User',
|
|
363
|
-
lastName: 'Userson',
|
|
364
|
-
county: 'IS',
|
|
365
|
-
},
|
|
366
|
-
},
|
|
367
|
-
message,
|
|
368
|
-
memo: 'Powered by Monerium SDK',
|
|
369
|
-
chain: 'ethereum',
|
|
370
|
-
network: 'sepolia',
|
|
371
|
-
// supportingDocumentId, see below
|
|
314
|
+
return accessToken;
|
|
315
|
+
}
|
|
372
316
|
});
|
|
373
|
-
```
|
|
374
|
-
|
|
375
|
-
API documentation:
|
|
376
|
-
|
|
377
|
-
- [GET /orders](https://docs.monerium.com/api#operation/orders)
|
|
378
|
-
- [POST /orders](https://docs.monerium.com/api#operation/post-orders)
|
|
379
317
|
|
|
380
|
-
|
|
318
|
+
// --- Server or Client: Initiate login ---
|
|
319
|
+
async function initiateAuth() {
|
|
320
|
+
const { codeChallenge, codeVerifier } = generatePKCE();
|
|
321
|
+
// Store `codeVerifier` in a secure, server-side session or cookie
|
|
322
|
+
// session.set('pkce_verifier', codeVerifier);
|
|
323
|
+
|
|
324
|
+
const authUrl = client.buildAuthorizationUrl({
|
|
325
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
326
|
+
redirectUri: 'https://your-app.com/callback',
|
|
327
|
+
codeChallenge: codeChallenge,
|
|
328
|
+
state: 'random_state_string'
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
// Redirect user to the returned URL...
|
|
332
|
+
// (e.g., `window.location.assign(authUrl)` on the client, or `redirect(authUrl)` on the server)
|
|
333
|
+
}
|
|
381
334
|
|
|
382
|
-
|
|
335
|
+
// --- Server: Handle Callback ---
|
|
336
|
+
// Exchanging the code requires a secure backend endpoint
|
|
337
|
+
async function handleAuthCallback(requestUrl: string) {
|
|
338
|
+
// 1. Parse Authorization Response
|
|
339
|
+
const { code } = client.parseAuthorizationResponse(requestUrl);
|
|
340
|
+
|
|
341
|
+
// 2. Retrieve the stored verifier
|
|
342
|
+
// const storedVerifier = session.get('pkce_verifier');
|
|
343
|
+
|
|
344
|
+
// 3. Exchange code for tokens
|
|
345
|
+
const tokens = await client.authorizationCodeGrant({
|
|
346
|
+
clientId: 'YOUR_CLIENT_ID',
|
|
347
|
+
redirectUri: 'https://your-app.com/callback',
|
|
348
|
+
code: code!,
|
|
349
|
+
codeVerifier: 'STORED_CODE_VERIFIER'
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
// 4. Save `tokens` to your database associated with the user
|
|
353
|
+
await db.saveUserTokens(userId, tokens);
|
|
354
|
+
}
|
|
383
355
|
|
|
384
|
-
|
|
385
|
-
//
|
|
386
|
-
const
|
|
387
|
-
|
|
356
|
+
async function runOAuthUserFlow() {
|
|
357
|
+
// 5. Link a Wallet
|
|
358
|
+
const { addresses } = await client.getAddresses();
|
|
359
|
+
if (!addresses.some(a => a.address === '0xUserAddress...')) {
|
|
360
|
+
await client.linkAddress({
|
|
361
|
+
address: '0xUserAddress...',
|
|
362
|
+
message: 'I hereby declare that I am the address owner.',
|
|
363
|
+
signature: '0xUserSignature...',
|
|
364
|
+
chain: 'arbitrum'
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
// 6. Check if Profile & IBAN are approved
|
|
369
|
+
const profiles = await client.getProfiles();
|
|
370
|
+
const defaultProfile = profiles.profiles[0];
|
|
371
|
+
const { ibans } = await client.getIbans();
|
|
372
|
+
const activeIban = ibans.find(i => i.state === 'approved');
|
|
373
|
+
|
|
374
|
+
if (defaultProfile?.state === 'approved' && activeIban) {
|
|
375
|
+
// 7. Get user's balances
|
|
376
|
+
const balances = await client.getBalances({
|
|
377
|
+
address: '0xUserAddress...',
|
|
378
|
+
chain: 'arbitrum'
|
|
379
|
+
});
|
|
380
|
+
|
|
381
|
+
console.log(balances);
|
|
382
|
+
|
|
383
|
+
// You can also place orders here safely!
|
|
384
|
+
} else if (defaultProfile?.state === 'approved' && !activeIban) {
|
|
385
|
+
// Automatically issue/move the IBAN if the profile is approved but the IBAN is not ready
|
|
386
|
+
if (ibans.length > 0) {
|
|
387
|
+
if (!ibans.some(i => i.address === '0xUserAddress...' && i.chain === 'arbitrum')) {
|
|
388
|
+
await client.moveIban({
|
|
389
|
+
iban: ibans[0].iban,
|
|
390
|
+
address: '0xUserAddress...',
|
|
391
|
+
chain: 'arbitrum'
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
} else {
|
|
395
|
+
await client.requestIban({
|
|
396
|
+
address: '0xUserAddress...',
|
|
397
|
+
chain: 'arbitrum'
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
}
|
|
388
402
|
```
|
|
389
403
|
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
- [/files](https://docs.monerium.com/api#operation/supporting-document)
|
|
404
|
+
### 4. Custom Transport
|
|
393
405
|
|
|
394
|
-
|
|
406
|
+
_Inject custom logic (retries, logging, proxies) by replacing the default fetch implementation._
|
|
395
407
|
|
|
396
408
|
```ts
|
|
397
|
-
import {
|
|
398
|
-
const [orderState, setOrderState] = useState<OrderState>();
|
|
409
|
+
import { MoneriumOAuthClient } from '@monerium/sdk';
|
|
399
410
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
profile: 'my-profile-id',
|
|
411
|
+
const api = new MoneriumOAuthClient({
|
|
412
|
+
environment: 'sandbox',
|
|
413
|
+
getAccessToken: async () => '...',
|
|
414
|
+
transport: async ({ method, url, headers, body }) => {
|
|
415
|
+
console.log(`Calling ${method} ${url}`);
|
|
416
|
+
const res = await fetch(url, { method, headers, body });
|
|
417
|
+
return { status: res.status, bodyText: await res.text() };
|
|
408
418
|
},
|
|
409
|
-
// optional callback functions
|
|
410
|
-
onMessage: (order) => console.log(order)
|
|
411
|
-
onError: (error) => console.error(error)
|
|
412
419
|
});
|
|
413
|
-
|
|
414
|
-
// Unsubscribe from specific order events
|
|
415
|
-
monerium.unsubscribeOrderNotifications({
|
|
416
|
-
state: OrderState.pending,
|
|
417
|
-
profile: 'my-profile-id'
|
|
418
|
-
});
|
|
419
|
-
// Unsubscribe from all order events
|
|
420
|
-
monerium.unsubscribeOrderNotifications();
|
|
421
|
-
|
|
422
420
|
```
|
|
423
421
|
|
|
424
422
|
## API Reference
|
|
425
423
|
|
|
426
|
-
[API Documentation](https://docs.monerium.com
|
|
424
|
+
[API Documentation](https://docs.monerium.com/api)
|
|
427
425
|
|
|
428
426
|
## Contributing
|
|
429
427
|
|
|
430
|
-
We are using
|
|
431
|
-
|
|
432
|
-
We are using PNPM as a package manager.
|
|
433
|
-
|
|
434
|
-
#### Development mode
|
|
435
|
-
|
|
436
|
-
```
|
|
437
|
-
pnpm dev
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
While in development mode, TypeScript declaration maps (`.d.ts.map`) are generated. TypeScript declaration maps are mainly used to quickly jump to type definitions in the context of a monorepo.
|
|
428
|
+
We are using PNPM as a package manager and [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) standard.
|
|
441
429
|
|
|
442
430
|
#### Build
|
|
443
431
|
|
|
@@ -445,26 +433,12 @@ While in development mode, TypeScript declaration maps (`.d.ts.map`) are generat
|
|
|
445
433
|
pnpm build
|
|
446
434
|
```
|
|
447
435
|
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
Refer to [Typedocs](https://typedoc.org/) syntaxes to use for this [documentation](https://monerium.github.io/js-monorepo/).
|
|
451
|
-
|
|
452
|
-
#### Publishing
|
|
453
|
-
|
|
454
|
-
When changes are merged to the `main` branch that follows the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standard, [release-please](https://github.com/googleapis/release-please) workflow creates a pull request, preparing for the next release. If kept open, the following commits will also be added to the PR. Merging that PR will create a new release, a workflow will publish it on NPM and tag it on Github.
|
|
436
|
+
#### Test
|
|
455
437
|
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
438
|
+
```
|
|
439
|
+
pnpm test
|
|
440
|
+
```
|
|
459
441
|
|
|
460
442
|
## Support
|
|
461
443
|
|
|
462
|
-
[Support](https://monerium.app/help)
|
|
463
|
-
|
|
464
|
-
[Telegram](https://t.me/+lGtM1gY9zWthNGE8)
|
|
465
|
-
|
|
466
|
-
[Github Issues](https://github.com/monerium/js-monorepo/issues)
|
|
467
|
-
|
|
468
|
-
## Release Notes
|
|
469
|
-
|
|
470
|
-
https://github.com/monerium/js-monorepo/releases
|
|
444
|
+
[Support](https://monerium.app/help) | [Github Issues](https://github.com/monerium/js-monorepo/issues)
|