suioutkit 1.0.4 → 1.2.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 +26 -15
- package/dist/config/api.d.ts +2 -2
- package/dist/config/modes.d.ts +6 -0
- package/dist/index.d.ts +3 -5
- package/dist/index.js +40 -7
- package/dist/types/index.d.ts +13 -1
- package/package.json +1 -1
- package/src/components/modal.ts +35 -5
- package/src/components/style.css +42 -0
- package/src/config/api.ts +2 -3
- package/src/config/modes.ts +16 -0
- package/src/index.ts +10 -6
- package/src/types/index.ts +15 -1
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
Browser SDK for SuiOutKit checkout: create sessions, open a ready-made payment modal, or build a custom UI with helpers.
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
Defaults to the **hosted SuiOutKit API** at `https://api.suioutkit.xyz` (`mode: "live"`). Switch with `mode: "test"` or `mode: "local"` for development. All routes under `/v1/`. The SDK does not perform settlement, treasury checks, or provider calls itself.
|
|
13
13
|
|
|
14
14
|
| Resource | Link |
|
|
15
15
|
|----------|------|
|
|
@@ -47,7 +47,7 @@ import { SuiOutKit } from "suioutkit";
|
|
|
47
47
|
|
|
48
48
|
const sdk = new SuiOutKit({
|
|
49
49
|
merchantAddress: "0xYOUR_MERCHANT_SUI_ADDRESS",
|
|
50
|
-
//
|
|
50
|
+
// mode optional - defaults to "live" (https://api.suioutkit.xyz, mainnet)
|
|
51
51
|
});
|
|
52
52
|
|
|
53
53
|
export function PayButton() {
|
|
@@ -86,14 +86,10 @@ For simple demos you can serve the built SDK bundle from any static host. Build
|
|
|
86
86
|
| Option | Type | Required | Description |
|
|
87
87
|
|--------|------|----------|-------------|
|
|
88
88
|
| `merchantAddress` | `string` | Yes | Sui address that receives settlement |
|
|
89
|
-
| `
|
|
89
|
+
| `mode` | `"local" \| "test" \| "live"` | No | Default: `"live"`. `"local"` → `http://localhost:5000` (testnet), `"test"` → `https://api.staging.suioutkit.xyz` (testnet), `"live"` → `https://api.suioutkit.xyz` (mainnet). |
|
|
90
|
+
| `backendUrl` | `string` | No | Override API origin (no trailing slash). Takes precedence over `mode`. |
|
|
90
91
|
|
|
91
|
-
|
|
92
|
-
The modal reads `window.SuiOutKitNetwork` - set to `"mainnet"` or `"testnet"` before opening the modal if you need a specific network for wallet / outPay flows.
|
|
93
|
-
|
|
94
|
-
```html
|
|
95
|
-
<script>window.SuiOutKitNetwork = "testnet";</script>
|
|
96
|
-
```
|
|
92
|
+
The SDK automatically sets `window.SuiOutKitNetwork` to the correct Sui network based on `mode` - no manual `<script>` tag needed.
|
|
97
93
|
|
|
98
94
|
## API reference
|
|
99
95
|
### `initCheckout(options)`
|
|
@@ -103,6 +99,7 @@ Creates a checkout session on the backend.
|
|
|
103
99
|
const session = await sdk.initCheckout({
|
|
104
100
|
amount: 45000, // integer in major units (e.g. 45000 NGN)
|
|
105
101
|
currency: "NGN", // e.g. "NGN"
|
|
102
|
+
coinType?: "0x2::sui::SUI", // optional: override settlement coin
|
|
106
103
|
metadata?: { orderId: "ORDER-123" },
|
|
107
104
|
});
|
|
108
105
|
```
|
|
@@ -116,6 +113,7 @@ const session = await sdk.initCheckout({
|
|
|
116
113
|
| `amount`, `currency` | Checkout totals |
|
|
117
114
|
| `merchantAddress` | Normalized Sui address |
|
|
118
115
|
| `coinType` | Settlement coin type (from backend config) |
|
|
116
|
+
| `supportedCoins` | Array of `{ symbol, type, decimals }` for available settlement coins |
|
|
119
117
|
| `estimatedRate` | FX preview (NGN → token) when applicable |
|
|
120
118
|
| `packageId`, `cryptoRegistryId`, `cryptoRegistryName` | On-chain config for crypto paths |
|
|
121
119
|
|
|
@@ -123,17 +121,29 @@ Throws if the backend returns a non-OK response.
|
|
|
123
121
|
|
|
124
122
|
---
|
|
125
123
|
|
|
126
|
-
### `openModal(session,
|
|
124
|
+
### `openModal(session, options?)`
|
|
127
125
|
Opens the built-in checkout modal (bank transfer, OPay, Stripe, Sui wallet, outPay).
|
|
128
126
|
|
|
129
127
|
```ts
|
|
130
|
-
const modal = sdk.openModal(session,
|
|
131
|
-
|
|
128
|
+
const modal = sdk.openModal(session, {
|
|
129
|
+
onClose: () => console.log("closed"),
|
|
130
|
+
onPaymentComplete: (result) => console.log(result.txDigest, result.walrusBlobId),
|
|
131
|
+
redirectUrl: "/thank-you",
|
|
132
|
+
autoCloseOnSuccess: true,
|
|
132
133
|
});
|
|
133
134
|
```
|
|
134
135
|
|
|
135
136
|
**Returns** `SuiOutKitModal` (internal handle). The modal loads styles from `{backendUrl}/style.css` automatically.
|
|
136
137
|
|
|
138
|
+
Options (`SuiOutKitModalOptions`):
|
|
139
|
+
|
|
140
|
+
| Option | Type | Description |
|
|
141
|
+
|--------|------|-------------|
|
|
142
|
+
| `onClose` | `() => void` | Called when the user dismisses the overlay |
|
|
143
|
+
| `onPaymentComplete` | `(result: PaymentResult) => void` | Called after on-chain settlement with `{ nonce, txDigest, walrusBlobId }` |
|
|
144
|
+
| `redirectUrl` | `string` | Redirect browser here after successful payment |
|
|
145
|
+
| `autoCloseOnSuccess` | `boolean` | Auto-close the modal after settlement instead of showing success panel |
|
|
146
|
+
|
|
137
147
|
> **Note:** Theme, logo, and `allowedMethods` customization are not exposed on `openModal` today. Use [custom UI](#custom-ui-no-modal) or extend the modal in source if you need that.
|
|
138
148
|
|
|
139
149
|
---
|
|
@@ -146,6 +156,7 @@ Binds checkout to a DOM button.
|
|
|
146
156
|
| `selector` | `string` | CSS selector (e.g. `"#pay-btn"`) |
|
|
147
157
|
| `options.amount` | `number` | Checkout amount |
|
|
148
158
|
| `options.currency` | `string` | e.g. `"NGN"` |
|
|
159
|
+
| `options.coinType` | `string` | Optional settlement coin type override |
|
|
149
160
|
| `options.metadata` | `object` | Optional passthrough to `initCheckout` |
|
|
150
161
|
|
|
151
162
|
---
|
|
@@ -258,7 +269,7 @@ Webhooks (`/v1/checkout/webhook`, `/v1/checkout/stripe-webhook`) are server-to-p
|
|
|
258
269
|
| `409 Treasury insufficient` | Operator vault underfunded for FX settlement amount |
|
|
259
270
|
| Modal stuck on “waiting for settlement” | Webhook not reaching backend (Flutterwave hash, Stripe CLI, or ngrok) |
|
|
260
271
|
| Stripe card errors on small NGN amounts | Backend enforces ~$0.50 USD equivalent minimum |
|
|
261
|
-
| Crypto connect fails | Wrong `
|
|
272
|
+
| Crypto connect fails | Wrong `mode` or registry env on backend |
|
|
262
273
|
| Styles missing | Backend not serving `/style.css` or wrong `backendUrl` |
|
|
263
274
|
|
|
264
275
|
See also [Developer Guide - Troubleshooting](/docs/developer-guide.md#troubleshooting).
|
|
@@ -266,9 +277,9 @@ See also [Developer Guide - Troubleshooting](/docs/developer-guide.md#troublesho
|
|
|
266
277
|
|
|
267
278
|
|
|
268
279
|
## Security
|
|
269
|
-
- Only `merchantAddress` and `backendUrl` belong in browser code.
|
|
280
|
+
- Only `merchantAddress`, `mode`, and `backendUrl` belong in browser code.
|
|
270
281
|
- Never embed operator keys, Flutterwave secrets, or Stripe secret keys in the client.
|
|
271
|
-
- Use **HTTPS** for
|
|
282
|
+
- Use **HTTPS** for the API endpoint in production (default when `mode: "live"`).
|
|
272
283
|
|
|
273
284
|
Report vulnerabilities through your project’s private security channel (do not file public issues with key material).
|
|
274
285
|
|
package/dist/config/api.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/** Production SuiOutKit API origin (versioned paths under /v1/). */
|
|
2
|
-
export declare const DEFAULT_API_ORIGIN = "
|
|
1
|
+
/** Production SuiOutKit API origin (versioned paths under /v1/). Use `mode` config to switch between local/test/live. */
|
|
2
|
+
export declare const DEFAULT_API_ORIGIN = "https://api.suioutkit.xyz";
|
|
3
3
|
/** API version prefix - all checkout and payment routes live under this path. */
|
|
4
4
|
export declare const API_V1_PREFIX = "/v1";
|
|
5
5
|
export declare function joinApiPath(origin: string, ...segments: string[]): string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
import { CheckoutSession, CheckoutSessionOptions, CryptoConfirmResponse, SuiOutKitModalOptions } from "./types/index.js";
|
|
1
|
+
import { CheckoutSession, CheckoutSessionOptions, CryptoConfirmResponse, SuiOutKitModalOptions, SuiOutKitConfig } from "./types/index.js";
|
|
2
2
|
import { SuiOutKitModal } from "./components/modal.js";
|
|
3
3
|
export { DEFAULT_API_ORIGIN, API_V1_PREFIX } from "./config/api.js";
|
|
4
4
|
export declare class SuiOutKit {
|
|
5
5
|
private backendUrl;
|
|
6
6
|
private merchantAddress;
|
|
7
|
-
constructor(config:
|
|
8
|
-
merchantAddress: string;
|
|
9
|
-
backendUrl?: string;
|
|
10
|
-
});
|
|
7
|
+
constructor(config: SuiOutKitConfig);
|
|
11
8
|
/**
|
|
12
9
|
* Initializes a brand-new isolated checkout session from the backend.
|
|
13
10
|
*/
|
|
@@ -27,6 +24,7 @@ export declare class SuiOutKit {
|
|
|
27
24
|
wrapButton(selector: string, options: {
|
|
28
25
|
amount: number;
|
|
29
26
|
currency: "NGN" | "SUI" | string;
|
|
27
|
+
coinType?: string;
|
|
30
28
|
metadata?: Record<string, any>;
|
|
31
29
|
}): void;
|
|
32
30
|
}
|
package/dist/index.js
CHANGED
|
@@ -51981,7 +51981,7 @@ var loadStripe = function loadStripe2() {
|
|
|
51981
51981
|
var import_react = __toESM(require_react());
|
|
51982
51982
|
|
|
51983
51983
|
// src/config/api.ts
|
|
51984
|
-
var DEFAULT_API_ORIGIN = "
|
|
51984
|
+
var DEFAULT_API_ORIGIN = "https://api.suioutkit.xyz";
|
|
51985
51985
|
var API_V1_PREFIX = "/v1";
|
|
51986
51986
|
function joinApiPath(origin, ...segments) {
|
|
51987
51987
|
const base = origin.replace(/\/+$/, "");
|
|
@@ -52401,13 +52401,20 @@ var SuiOutKitModal = class {
|
|
|
52401
52401
|
async handleCryptoPaymentPanel() {
|
|
52402
52402
|
const container = this.overlay?.querySelector("#sok-content-panel");
|
|
52403
52403
|
if (!container) return;
|
|
52404
|
+
const coins = this.session.supportedCoins || [];
|
|
52405
|
+
const currentCoin = this.session.coinType || "0x2::sui::SUI";
|
|
52406
|
+
const currentSymbol = coins.find((c4) => c4.type === currentCoin)?.symbol || "SUI";
|
|
52407
|
+
const coinChips = coins.length > 1 ? `<div class="sok-coin-selector">${coins.map(
|
|
52408
|
+
(c4) => `<button class="sok-coin-chip${c4.type === currentCoin ? " active" : ""}" data-coin-type="${c4.type}">${c4.symbol}</button>`
|
|
52409
|
+
).join("")}</div>` : `<div class="sok-coin-badge">${currentSymbol}</div>`;
|
|
52404
52410
|
container.innerHTML = `
|
|
52405
52411
|
<button class="suioutkit-back" id="sok-back-btn">\u2190 Back to methods</button>
|
|
52406
52412
|
<div class="suioutkit-header">
|
|
52407
52413
|
<h2 class="suioutkit-title">Pay with Sui Wallet</h2>
|
|
52408
|
-
<p class="suioutkit-subtitle">Choose
|
|
52414
|
+
<p class="suioutkit-subtitle">Choose settlement token and payment channel</p>
|
|
52409
52415
|
</div>
|
|
52410
52416
|
<div class="suioutkit-panel">
|
|
52417
|
+
${coinChips}
|
|
52411
52418
|
<p class="sok-status-text" style="margin-bottom: 12px;">
|
|
52412
52419
|
Choose whether to pay via a desktop extension wallet or scan a dynamic QR Code with your mobile wallet.
|
|
52413
52420
|
</p>
|
|
@@ -52419,6 +52426,17 @@ var SuiOutKitModal = class {
|
|
|
52419
52426
|
</button>
|
|
52420
52427
|
</div>
|
|
52421
52428
|
`;
|
|
52429
|
+
if (coins.length > 1) {
|
|
52430
|
+
container.querySelectorAll(".sok-coin-chip").forEach((chip) => {
|
|
52431
|
+
chip.addEventListener("click", () => {
|
|
52432
|
+
const coinType = chip.dataset.coinType || "";
|
|
52433
|
+
if (coinType && coinType !== this.session.coinType) {
|
|
52434
|
+
this.session.coinType = coinType;
|
|
52435
|
+
void this.handleCryptoPaymentPanel();
|
|
52436
|
+
}
|
|
52437
|
+
});
|
|
52438
|
+
});
|
|
52439
|
+
}
|
|
52422
52440
|
container.querySelector("#sok-back-btn")?.addEventListener("click", () => this.renderSelectionPanel());
|
|
52423
52441
|
container.querySelector("#sok-connect-extension-btn")?.addEventListener("click", async () => {
|
|
52424
52442
|
this.renderLoadingPanel("Preparing crypto payment...");
|
|
@@ -52766,13 +52784,17 @@ var SuiOutKitModal = class {
|
|
|
52766
52784
|
});
|
|
52767
52785
|
}
|
|
52768
52786
|
async loadCryptoIntent(method) {
|
|
52787
|
+
const body = {
|
|
52788
|
+
token: this.session.token,
|
|
52789
|
+
method
|
|
52790
|
+
};
|
|
52791
|
+
if (this.session.coinType) {
|
|
52792
|
+
body.coinType = this.session.coinType;
|
|
52793
|
+
}
|
|
52769
52794
|
const response = await fetch(joinApiPath(this.backendUrl, "checkout", "crypto", "intent"), {
|
|
52770
52795
|
method: "POST",
|
|
52771
52796
|
headers: { "Content-Type": "application/json" },
|
|
52772
|
-
body: JSON.stringify(
|
|
52773
|
-
token: this.session.token,
|
|
52774
|
-
method
|
|
52775
|
-
})
|
|
52797
|
+
body: JSON.stringify(body)
|
|
52776
52798
|
});
|
|
52777
52799
|
const result = await response.json();
|
|
52778
52800
|
if (!response.ok) {
|
|
@@ -52890,6 +52912,13 @@ var SuiOutKitModal = class {
|
|
|
52890
52912
|
}
|
|
52891
52913
|
};
|
|
52892
52914
|
|
|
52915
|
+
// src/config/modes.ts
|
|
52916
|
+
var MODE_MAP = {
|
|
52917
|
+
local: { backendUrl: "http://localhost:5000", suiNetwork: "testnet" },
|
|
52918
|
+
test: { backendUrl: "https://api.staging.suioutkit.xyz", suiNetwork: "testnet" },
|
|
52919
|
+
live: { backendUrl: "https://api.suioutkit.xyz", suiNetwork: "mainnet" }
|
|
52920
|
+
};
|
|
52921
|
+
|
|
52893
52922
|
// src/utils/http.ts
|
|
52894
52923
|
var HttpError = class extends Error {
|
|
52895
52924
|
constructor(message, status = null, body = null) {
|
|
@@ -52990,8 +53019,11 @@ var SuiOutKit = class {
|
|
|
52990
53019
|
if (!config.merchantAddress) {
|
|
52991
53020
|
throw new Error("SuiOutKit Error: merchantAddress is required.");
|
|
52992
53021
|
}
|
|
52993
|
-
|
|
53022
|
+
const mode = config.mode || "live";
|
|
53023
|
+
const modeCfg = MODE_MAP[mode];
|
|
53024
|
+
this.backendUrl = (config.backendUrl || modeCfg.backendUrl).replace(/\/+$/, "");
|
|
52994
53025
|
this.merchantAddress = config.merchantAddress;
|
|
53026
|
+
window.SuiOutKitNetwork = modeCfg.suiNetwork;
|
|
52995
53027
|
}
|
|
52996
53028
|
/**
|
|
52997
53029
|
* Initializes a brand-new isolated checkout session from the backend.
|
|
@@ -53005,6 +53037,7 @@ var SuiOutKit = class {
|
|
|
53005
53037
|
amount: options.amount,
|
|
53006
53038
|
currency: options.currency,
|
|
53007
53039
|
merchantAddress: this.merchantAddress,
|
|
53040
|
+
coinType: options.coinType,
|
|
53008
53041
|
metadata: options.metadata || {}
|
|
53009
53042
|
})
|
|
53010
53043
|
});
|
package/dist/types/index.d.ts
CHANGED
|
@@ -1,8 +1,19 @@
|
|
|
1
|
-
export type
|
|
1
|
+
export type SuiOutKitMode = "local" | "test" | "live";
|
|
2
|
+
export interface SuiOutKitConfig {
|
|
3
|
+
merchantAddress: string;
|
|
4
|
+
mode?: SuiOutKitMode;
|
|
5
|
+
backendUrl?: string;
|
|
6
|
+
}
|
|
7
|
+
export interface SupportedCoin {
|
|
8
|
+
symbol: string;
|
|
9
|
+
type: string;
|
|
10
|
+
decimals: number;
|
|
11
|
+
}
|
|
2
12
|
export interface CheckoutSessionOptions {
|
|
3
13
|
amount: number;
|
|
4
14
|
currency: "NGN" | "SUI" | string;
|
|
5
15
|
merchantAddress: string;
|
|
16
|
+
coinType?: string;
|
|
6
17
|
metadata?: Record<string, any>;
|
|
7
18
|
}
|
|
8
19
|
export interface CheckoutSession {
|
|
@@ -17,6 +28,7 @@ export interface CheckoutSession {
|
|
|
17
28
|
cryptoRegistryName?: string;
|
|
18
29
|
coinType?: string;
|
|
19
30
|
estimatedRate?: number;
|
|
31
|
+
supportedCoins?: SupportedCoin[];
|
|
20
32
|
}
|
|
21
33
|
export type ChargeMethod = "bank_transfer" | "opay" | "crypto" | "sui_wallet" | "outpay" | "stripe";
|
|
22
34
|
export interface VirtualAccount {
|
package/package.json
CHANGED
package/src/components/modal.ts
CHANGED
|
@@ -420,13 +420,27 @@ export class SuiOutKitModal {
|
|
|
420
420
|
const container = this.overlay?.querySelector("#sok-content-panel");
|
|
421
421
|
if (!container) return;
|
|
422
422
|
|
|
423
|
+
const coins = this.session.supportedCoins || [];
|
|
424
|
+
const currentCoin = this.session.coinType || "0x2::sui::SUI";
|
|
425
|
+
const currentSymbol = coins.find((c) => c.type === currentCoin)?.symbol || "SUI";
|
|
426
|
+
|
|
427
|
+
const coinChips = coins.length > 1
|
|
428
|
+
? `<div class="sok-coin-selector">${coins
|
|
429
|
+
.map(
|
|
430
|
+
(c) =>
|
|
431
|
+
`<button class="sok-coin-chip${c.type === currentCoin ? " active" : ""}" data-coin-type="${c.type}">${c.symbol}</button>`
|
|
432
|
+
)
|
|
433
|
+
.join("")}</div>`
|
|
434
|
+
: `<div class="sok-coin-badge">${currentSymbol}</div>`;
|
|
435
|
+
|
|
423
436
|
container.innerHTML = `
|
|
424
437
|
<button class="suioutkit-back" id="sok-back-btn">← Back to methods</button>
|
|
425
438
|
<div class="suioutkit-header">
|
|
426
439
|
<h2 class="suioutkit-title">Pay with Sui Wallet</h2>
|
|
427
|
-
<p class="suioutkit-subtitle">Choose
|
|
440
|
+
<p class="suioutkit-subtitle">Choose settlement token and payment channel</p>
|
|
428
441
|
</div>
|
|
429
442
|
<div class="suioutkit-panel">
|
|
443
|
+
${coinChips}
|
|
430
444
|
<p class="sok-status-text" style="margin-bottom: 12px;">
|
|
431
445
|
Choose whether to pay via a desktop extension wallet or scan a dynamic QR Code with your mobile wallet.
|
|
432
446
|
</p>
|
|
@@ -439,6 +453,18 @@ export class SuiOutKitModal {
|
|
|
439
453
|
</div>
|
|
440
454
|
`;
|
|
441
455
|
|
|
456
|
+
if (coins.length > 1) {
|
|
457
|
+
container.querySelectorAll(".sok-coin-chip").forEach((chip) => {
|
|
458
|
+
chip.addEventListener("click", () => {
|
|
459
|
+
const coinType = (chip as HTMLElement).dataset.coinType || "";
|
|
460
|
+
if (coinType && coinType !== this.session.coinType) {
|
|
461
|
+
this.session.coinType = coinType;
|
|
462
|
+
void this.handleCryptoPaymentPanel();
|
|
463
|
+
}
|
|
464
|
+
});
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
|
|
442
468
|
container.querySelector("#sok-back-btn")?.addEventListener("click", () => this.renderSelectionPanel());
|
|
443
469
|
|
|
444
470
|
container.querySelector("#sok-connect-extension-btn")?.addEventListener("click", async () => {
|
|
@@ -858,13 +884,17 @@ export class SuiOutKitModal {
|
|
|
858
884
|
}
|
|
859
885
|
|
|
860
886
|
private async loadCryptoIntent(method: "sui_wallet" | "outpay"): Promise<CryptoIntentResponse> {
|
|
887
|
+
const body: Record<string, any> = {
|
|
888
|
+
token: this.session.token,
|
|
889
|
+
method
|
|
890
|
+
};
|
|
891
|
+
if (this.session.coinType) {
|
|
892
|
+
body.coinType = this.session.coinType;
|
|
893
|
+
}
|
|
861
894
|
const response = await fetch(joinApiPath(this.backendUrl, "checkout", "crypto", "intent"), {
|
|
862
895
|
method: "POST",
|
|
863
896
|
headers: { "Content-Type": "application/json" },
|
|
864
|
-
body: JSON.stringify(
|
|
865
|
-
token: this.session.token,
|
|
866
|
-
method
|
|
867
|
-
})
|
|
897
|
+
body: JSON.stringify(body)
|
|
868
898
|
});
|
|
869
899
|
|
|
870
900
|
const result: any = await response.json();
|
package/src/components/style.css
CHANGED
|
@@ -478,6 +478,48 @@
|
|
|
478
478
|
100% { top: 0; }
|
|
479
479
|
}
|
|
480
480
|
|
|
481
|
+
.sok-coin-selector {
|
|
482
|
+
display: flex;
|
|
483
|
+
gap: 8px;
|
|
484
|
+
justify-content: center;
|
|
485
|
+
margin-bottom: 14px;
|
|
486
|
+
flex-wrap: wrap;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.sok-coin-chip {
|
|
490
|
+
background: var(--sok-card-bg);
|
|
491
|
+
border: 2px solid var(--sok-border);
|
|
492
|
+
border-radius: 20px;
|
|
493
|
+
padding: 6px 18px;
|
|
494
|
+
font-size: 13px;
|
|
495
|
+
font-weight: 600;
|
|
496
|
+
cursor: pointer;
|
|
497
|
+
transition: all 0.2s ease;
|
|
498
|
+
color: var(--sok-text);
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
.sok-coin-chip:hover {
|
|
502
|
+
border-color: var(--sok-accent);
|
|
503
|
+
color: var(--sok-accent);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.sok-coin-chip.active {
|
|
507
|
+
border-color: var(--sok-accent);
|
|
508
|
+
background: var(--sok-accent);
|
|
509
|
+
color: #ffffff;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.sok-coin-badge {
|
|
513
|
+
display: inline-block;
|
|
514
|
+
background: var(--sok-accent);
|
|
515
|
+
color: #ffffff;
|
|
516
|
+
border-radius: 20px;
|
|
517
|
+
padding: 4px 16px;
|
|
518
|
+
font-size: 13px;
|
|
519
|
+
font-weight: 600;
|
|
520
|
+
margin-bottom: 14px;
|
|
521
|
+
}
|
|
522
|
+
|
|
481
523
|
.suioutkit-wallet-list {
|
|
482
524
|
display: flex;
|
|
483
525
|
flex-direction: column;
|
package/src/config/api.ts
CHANGED
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
// Copyright (c) 2026 The3rdWebLabs (https://github.com/the3rdweblabs)
|
|
3
3
|
// Author: @CYBWithFlourish (https://github.com/CYBWithFlourish)
|
|
4
4
|
|
|
5
|
-
/** Production SuiOutKit API origin (versioned paths under /v1/). */
|
|
6
|
-
|
|
7
|
-
export const DEFAULT_API_ORIGIN = "http://localhost:5000";
|
|
5
|
+
/** Production SuiOutKit API origin (versioned paths under /v1/). Use `mode` config to switch between local/test/live. */
|
|
6
|
+
export const DEFAULT_API_ORIGIN = "https://api.suioutkit.xyz";
|
|
8
7
|
|
|
9
8
|
/** API version prefix - all checkout and payment routes live under this path. */
|
|
10
9
|
export const API_V1_PREFIX = "/v1";
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0
|
|
2
|
+
// Copyright (c) 2026 The3rdWebLabs (https://github.com/the3rdweblabs)
|
|
3
|
+
// Author: @CYBWithFlourish (https://github.com/CYBWithFlourish)
|
|
4
|
+
|
|
5
|
+
export type SuiOutKitMode = "local" | "test" | "live";
|
|
6
|
+
|
|
7
|
+
export interface ModeConfig {
|
|
8
|
+
backendUrl: string;
|
|
9
|
+
suiNetwork: "mainnet" | "testnet";
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const MODE_MAP: Record<SuiOutKitMode, ModeConfig> = {
|
|
13
|
+
local: { backendUrl: "http://localhost:5000", suiNetwork: "testnet" },
|
|
14
|
+
test: { backendUrl: "https://api.staging.suioutkit.xyz", suiNetwork: "testnet" },
|
|
15
|
+
live: { backendUrl: "https://api.suioutkit.xyz", suiNetwork: "mainnet" },
|
|
16
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,10 @@
|
|
|
2
2
|
// Copyright (c) 2026 The3rdWebLabs (https://github.com/the3rdweblabs)
|
|
3
3
|
// Author: @CYBWithFlourish (https://github.com/CYBWithFlourish)
|
|
4
4
|
|
|
5
|
-
import { CheckoutSession, CheckoutSessionOptions, CryptoConfirmResponse, SuiOutKitModalOptions } from "./types/index.js";
|
|
5
|
+
import { CheckoutSession, CheckoutSessionOptions, CryptoConfirmResponse, SuiOutKitModalOptions, SuiOutKitConfig, SuiOutKitMode } from "./types/index.js";
|
|
6
6
|
import { SuiOutKitModal } from "./components/modal.js";
|
|
7
|
-
import {
|
|
7
|
+
import { joinApiPath, DEFAULT_API_ORIGIN } from "./config/api.js";
|
|
8
|
+
import { MODE_MAP } from "./config/modes.js";
|
|
8
9
|
|
|
9
10
|
export { DEFAULT_API_ORIGIN, API_V1_PREFIX } from "./config/api.js";
|
|
10
11
|
|
|
@@ -12,13 +13,15 @@ export class SuiOutKit {
|
|
|
12
13
|
private backendUrl: string;
|
|
13
14
|
private merchantAddress: string;
|
|
14
15
|
|
|
15
|
-
constructor(config:
|
|
16
|
+
constructor(config: SuiOutKitConfig) {
|
|
16
17
|
if (!config.merchantAddress) {
|
|
17
18
|
throw new Error("SuiOutKit Error: merchantAddress is required.");
|
|
18
19
|
}
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
const mode: SuiOutKitMode = config.mode || "live";
|
|
21
|
+
const modeCfg = MODE_MAP[mode];
|
|
22
|
+
this.backendUrl = (config.backendUrl || modeCfg.backendUrl).replace(/\/+$/, "");
|
|
21
23
|
this.merchantAddress = config.merchantAddress;
|
|
24
|
+
(window as any).SuiOutKitNetwork = modeCfg.suiNetwork;
|
|
22
25
|
}
|
|
23
26
|
|
|
24
27
|
/**
|
|
@@ -33,6 +36,7 @@ export class SuiOutKit {
|
|
|
33
36
|
amount: options.amount,
|
|
34
37
|
currency: options.currency,
|
|
35
38
|
merchantAddress: this.merchantAddress,
|
|
39
|
+
coinType: options.coinType,
|
|
36
40
|
metadata: options.metadata || {}
|
|
37
41
|
})
|
|
38
42
|
});
|
|
@@ -86,7 +90,7 @@ export class SuiOutKit {
|
|
|
86
90
|
*/
|
|
87
91
|
public wrapButton(
|
|
88
92
|
selector: string,
|
|
89
|
-
options: { amount: number; currency: "NGN" | "SUI" | string; metadata?: Record<string, any> }
|
|
93
|
+
options: { amount: number; currency: "NGN" | "SUI" | string; coinType?: string; metadata?: Record<string, any> }
|
|
90
94
|
): void {
|
|
91
95
|
const btn = document.querySelector(selector) as HTMLButtonElement;
|
|
92
96
|
if (!btn) {
|
package/src/types/index.ts
CHANGED
|
@@ -2,12 +2,25 @@
|
|
|
2
2
|
// Copyright (c) 2026 The3rdWebLabs (https://github.com/the3rdweblabs)
|
|
3
3
|
// Author: @CYBWithFlourish (https://github.com/CYBWithFlourish)
|
|
4
4
|
|
|
5
|
-
export type
|
|
5
|
+
export type SuiOutKitMode = "local" | "test" | "live";
|
|
6
|
+
|
|
7
|
+
export interface SuiOutKitConfig {
|
|
8
|
+
merchantAddress: string;
|
|
9
|
+
mode?: SuiOutKitMode;
|
|
10
|
+
backendUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SupportedCoin {
|
|
14
|
+
symbol: string;
|
|
15
|
+
type: string;
|
|
16
|
+
decimals: number;
|
|
17
|
+
}
|
|
6
18
|
|
|
7
19
|
export interface CheckoutSessionOptions {
|
|
8
20
|
amount: number;
|
|
9
21
|
currency: "NGN" | "SUI" | string;
|
|
10
22
|
merchantAddress: string;
|
|
23
|
+
coinType?: string;
|
|
11
24
|
metadata?: Record<string, any>;
|
|
12
25
|
}
|
|
13
26
|
|
|
@@ -23,6 +36,7 @@ export interface CheckoutSession {
|
|
|
23
36
|
cryptoRegistryName?: string;
|
|
24
37
|
coinType?: string;
|
|
25
38
|
estimatedRate?: number;
|
|
39
|
+
supportedCoins?: SupportedCoin[];
|
|
26
40
|
}
|
|
27
41
|
|
|
28
42
|
export type ChargeMethod = "bank_transfer" | "opay" | "crypto" | "sui_wallet" | "outpay" | "stripe";
|