@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 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"></source>
9
- <img src="https://img.shields.io/badge/Developer_portal-2c6ca7" alt="Static Badge"></img>
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"></source>
15
- <img src="https://img.shields.io/badge/API_documentation-2c6ca7" alt="Static Badge"></img>
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></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"></source>
22
- <img src="https://img.shields.io/npm/v/%40monerium%2Fsdk?colorA=f6f8fa&colorB=f6f8fa" alt="Version"></img>
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"></source>
28
- <img src="https://img.shields.io/github/issues/monerium/js-monorepo?colorA=2c6ca7&colorB=21262d" alt="Version"></img>
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
- yarn add @monerium/sdk
42
+ pnpm add @monerium/sdk
64
43
  ```
65
44
 
66
- ## Configuration
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
- ### Environments - Networks
47
+ This section demonstrates how to use the plan-specific client classes (`MoneriumPrivateClient`, `MoneriumWhitelabelClient`, and `MoneriumOAuthClient`) for the integration flows.
76
48
 
77
- | Environment | Chain | Name |
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
- ## Usage Examples
51
+ [Read the Private Integration Guide](https://docs.monerium.com/private)
93
52
 
94
- We recommend starting in the [Developer Portal](https://docs.monerium.com). There, you will learn more about `client_id`'s and ways of authenticating.
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
- #### Initialize and authenticate using Client Credentials
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
- > Client Credentials is used when there's no need for user interaction, and the system-to-system interaction requires authentication.
58
+ ```typescript
59
+ import { MoneriumPrivateClient, Currency } from '@monerium/sdk';
99
60
 
100
- ```ts
101
- // Current version - Deprecated in v4
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
- if (Date.now() > session.accessExpiry) {
171
- const newProfile = await refreshTokenGrant({
172
- environment: 'sandbox',
173
- clientId: 'your-client-id',
174
- refreshToken: session.refreshToken,
175
- });
176
- session.accessToken = newProfile.access_token;
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
- > Authorization Code Flow with PKCE is used for apps where direct user interaction is involved, and the application is running on an environment where the confidentiality of a secret cannot be safely maintained. It allows the application to authorize users without handling their passwords and mitigates the additional risk involved in this sort of delegation.
194
-
195
- First, you have to navigate the user to the Monerium authentication flow. This can be done by generating a URL and redirecting the user to it. After the user has authenticated, Monerium will redirect back to your specified URI with a code. You can then finalize the authentication process by exchanging the code for access and refresh tokens.
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
- ```ts
198
- import { MoneriumClient } from '@monerium/sdk';
199
-
200
- export function App() {
201
- const [profiles, setProfiles] = useState<Profile[] | null>(null);
202
- const [monerium, setMonerium] = useState<MoneriumClient>();
203
- const [isAuthorized, setIsAuthorized] = useState<boolean>(false);
204
-
205
- useEffect(() => {
206
- const sdk = new MoneriumClient({
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
- setMonerium(sdk);
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
- return () => {
224
- if (monerium) {
225
- monerium.disconnect();
226
- }
227
- };
228
- }, [monerium]);
229
-
230
- useEffect(() => {
231
- const fetchData = async () => {
232
- if (monerium && isAuthorized) {
233
- try {
234
- const { profiles } = await monerium.getProfiles();
235
- setProfiles(profiles);
236
- } catch (err) {
237
- console.error('Error fetching data:', err);
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
- fetchData();
242
- }, [monerium, isAuthorized]);
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
- // Fetching all accounts for a specific profile
266
- const { id: profileId, accounts }: Profile = await monerium.getProfile(
267
- profiles[0].id
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
- // Fetching all balances for a specific profile
271
- const balances: Balances = await monerium.getBalances();
167
+ if (event.type === 'order.updated') {
168
+ console.log('Order status changed:', event.meta?.state);
169
+ }
170
+ }
272
171
  ```
273
172
 
274
- API documentation:
275
-
276
- - [/profile](https://docs.monerium.com/api#operation/profile)
277
- - [/profile/&#123;profileId&#123;/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
- ```ts
284
- const tokens: Token[] = await monerium.getTokens();
285
- ```
175
+ [Read the Whitelabel Integration Guide](https://docs.monerium.com/whitelabel)
286
176
 
287
- API documentation:
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
- - [/tokens](https://docs.monerium.com/api#operation/tokens)
180
+ Similar to the Private plan, use the `getAccessToken` pattern for efficient, secure server-to-server authentication.
290
181
 
291
- #### Link a new address to Monerium
182
+ ```typescript
183
+ import { MoneriumWhitelabelClient } from '@monerium/sdk';
292
184
 
293
- It's important to understand when interacting with a blockchain, the user needs to provide a signature in their wallet.
294
- This signature is used to verify that the user is the owner of the wallet address.
295
-
296
- We recommend Viem as an Ethereum interface, see: https://viem.sh/docs/actions/wallet/signMessage.html
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
- ```ts
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
- import { constants } from '@monerium/sdk';
301
- import { walletClient } from '...' // See Viem documentation
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
- const { LINK_MESSAGE } = constants; // "I hereby declare that I am the address owner."
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
- // Send a signature request to the wallet.
306
- const signature = await walletClient.signMessage({
307
- message: LINK_MESSAGE,
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
- // Link a new address to Monerium and create accounts for ethereum and gnosis.
311
- await monerium.linkAddress({
312
- profile: 'your-profile-id',
313
- address: '0xUserAddress72413Fa92980B889A1eCE84dD', // user wallet address
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
- API documentation:
321
-
322
- - [/profile/&#123;profileId&#123;/addresses](https://docs.monerium.com/api#operation/profile-addresses)
284
+ ### 3. OAuth Plan
323
285
 
324
- #### Get and place orders
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
- ```ts
332
- // Place a redeem order
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
- const amount = '100'; // replace with the amount in EUR
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
- // First you have to form the message that will be signed by the user
340
- const message = placeOrderMessage(amount, 'eur', iban);
293
+ ```typescript
294
+ import { MoneriumOAuthClient, generatePKCE } from '@monerium/sdk';
341
295
 
342
- // The message should look like this, with the current date and time in RFC3339 format:
343
- // Send EUR 100 to EE12341234123412341234 at Thu, 29 Dec 2022 14:58:29Z
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
- // Send a signature request to the wallet.
346
- const signature = await walletClient.signMessage({
347
- message: message,
348
- });
310
+ accessToken = auth.access_token;
311
+ await db.saveUserTokens(userId, auth);
312
+ }
349
313
 
350
- // Place the order
351
- const order = await monerium.placeOrder({
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
- #### Add supporting documents
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
- When placing orders with payouts above 15,000 EUR, a supporting document is required. The document must be uploaded to Monerium before the order can be placed. Supporting documents can be an invoice or an agreement.
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
- ```ts
385
- // Upload a supporting document
386
- const supportingDocumentId: SupportingDoc =
387
- await uploadSupportingDocument(document);
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
- API documentation:
391
-
392
- - [/files](https://docs.monerium.com/api#operation/supporting-document)
404
+ ### 4. Custom Transport
393
405
 
394
- #### Subscribe to order events
406
+ _Inject custom logic (retries, logging, proxies) by replacing the default fetch implementation._
395
407
 
396
408
  ```ts
397
- import { OrderState } from '@monerium/sdk';
398
- const [orderState, setOrderState] = useState<OrderState>();
409
+ import { MoneriumOAuthClient } from '@monerium/sdk';
399
410
 
400
- // Subscribe to all order events
401
- monerium.subscribeOrderNotifications();
402
-
403
- // Subscribe to specific order events
404
- monerium.subscribeOrderNotifications({ 
405
- filter: {
406
- state: OrderState.pending,
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//api)
424
+ [API Documentation](https://docs.monerium.com/api)
427
425
 
428
426
  ## Contributing
429
427
 
430
- We are using [commitlint](https://github.com/conventional-changelog/commitlint/tree/master/@commitlint/config-conventional) to enforce that developers format the commit messages according to the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) guidelines.
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
- ### Documentation
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
- ## FAQs
457
-
458
- Common questions developers have regarding the SDK.
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)