To setup the platform demo and try it out follow these steps:
npm run i:all
cp ./server/.env.sample ./server/.env # add your Sellix API key in this file
npm run devThen you can go to http://localhost:9998 and press the signup button.
platform= the platform that will use Sellix as wallet providermerchant_id= the user's id in the Sellix ecosystem, every platform user will need a Sellixmerchant_idto use the walletmerchant= the user that will use the platform and will be associated with a Sellixmerchant_idmerchant_token= the token that will be used to authenticate the merchant during the wallet setup and all the other wallet flows, the token has an expiration time and can be refreshedSellix API key= the platform owner will be able to retrieve his API key from the Sellix dashboard, the API key will be used by the platform to communicate with the Sellix backend
These are the two endpoints that the platform server will need to expose to the platform frontend:
createMerchantgetMerchantDashboard
- implementation here
- the platform frontend calls the platform server to create a new user that will be associated with the Sellix
merchant_id - the platform server calls Sellix API using the
Sellix API key(/wallet/createWalletMerchant) that returns amerchant_idand amerchant_tokenthat will be used to authenticate the merchant - the platform frontend will render Sellix wallet
/setuppage in an iframe with themerchant_tokenas query param - the merchant will proceed with a regular wallet setup
- when the setup is completed the wallet makes a call directly to Sellix API (instead of using postMessage) to upload the addresses using the
merchant_token:/wallet/addressfor all the primary addresses/wallet/reusable_addressesfor NANO and SOLANA reusable addresses
- concordium will not be setup at this point because it requires a different setup flow (see below)
sequenceDiagram
box Platform
participant frontend
participant backend
end
frontend->>backend: create a new merchant on the platform
backend->>sellix-backend: createWalletMerchant
activate sellix-backend
sellix-backend->>backend: return merchant_id and merchant_token
deactivate sellix-backend
backend->>frontend: return walletUrl with merchant_token in qs
activate frontend
frontend->>wallet: render wallet /setup with token in qs
wallet->>sellix-backend: send user addresses using token auth
wallet->>frontend: return OK message if user is setup
deactivate frontend
- implementation here
- the merchant calls the platform server to get the wallet url
- the platform server calls Sellix API using the
Sellix API key(/wallet/getWalletMerchant) that returns amerchant_idand amerchant_tokenthat will be used to authenticate the merchant - the platform frontend renders the wallet iframe using the wallet url with the merchant token
- the wallet uses the merchant token (instead of receiving postMessage) to make the
/wallet/selfAPI call to get the merchant addresses created during the setup phase - the wallet uses the merchant token (instead of receiving postMessage) to make the
/wallet/estimate/<currency>/reusable_addressesAPI call to check if there is the need to upload new addresses - now the wallet is able to perform the following operations in a seameless way like you would do in the Sellix dashboard:
- pressing the send button in the
/balancesscreen the merchant will see the payout screen - from here the merchant can select the currency and the address to send the funds to.
- pressing the redeem button in the
/balancesscreen the merchant will see the redeem screen - the merchant can now redeem his SOLANA or NANO pending funds in his wallet
- if during the setup flow some cryptos weren't configured due to some problem, their corresponding row will appear at the bottom of the
/balancesscreen in theadditional cryptossection - the merchant presses on the row and inserts his mnemonic to generate the addresses for the missing cryptos
- if the merchant forgets his mnemonic he can press the
reset mnemonicbutton when prompted to. - the merchant can regenerate his addresses using a new mnemonic
- TODO: link to reusable addresses documentation
- if the merchant needs to add more reusable addresses he can press the
add more addressesbutton when prompted to. - the merchant will be prompted to insert his mnemonic to generate more addresses
sequenceDiagram
box Platform
participant frontend
participant backend
end
frontend->>backend: makes getMerchantDashboard request
backend->>sellix-backend: /wallet/getWalletMerchant
activate sellix-backend
sellix-backend->>backend: return merchant_token
deactivate sellix-backend
backend->>frontend: return walletUrl with merchant_token
activate frontend
frontend->>wallet: render wallet/balances with token in qs
Note right of wallet: the merchant can now <br /> payout, receive, <br /> see transactions, <br /> reset mnemonic, <br /> ecc...
wallet-->>frontend: frontend can still keep track of iframe location listening for GO_TO postMessage
wallet-->>frontend: frontend can still use dark mode if needed
deactivate frontend
The wallet iframe will emit a GO_TO postMessage to the platform frontend so that the platform can keep track of the wallet current navigation state, this way the platform frontend can change its UI accordingly to the current wallet page
The wallet iframe can be rendered adding a dark = true query parameter to the wallet url, this way the wallet will be rendered in dark mode
Payments work with the standard Sellix Payments API integration, the platform only has to pass the additional field platform_on_behalf_of, which equals to the ID of the platform-merchant created through the create merchant endpoint.
It will return a checkout URL where you can redirect the customer to send the payment.
The platform can also pass any custom fee when creating the payment, which will be charged to the customer.
{
"platform_fee": {
"percentage": 5,
"fixed_fee": 0.10
}
}The fixed fee is always taken as USD, it will be added on top of any percentage passed.
Sample code snippets
PHP
<?php
$curl = curl_init();
$data = [
"platform_on_behalf_of" => "plat_149ffc-d0acb95171-sample",
"title" => "Platform Payment",
"value" => 2.50,
"currency" => "USD",
"platform_fee" => [
"percentage" => 5,
"fixed_fee" => 0.10
],
"email" => "example@gmail.com",
"white_label" => false,
"webhook" => "https://webhook.sellix.io/api/v1/example",
"return_url" => "https://sample.sellix.io/success"
]
curl_setopt_array($curl, array(
CURLOPT_URL => 'https://dev.sellix.io/v1/payments',
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => '',
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 0,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => 'POST',
CURLOPT_POSTFIELDS => json_encode($data),
CURLOPT_HTTPHEADER => array(
'Content-Type: application/json',
'Authorization: Bearer YOUR_API_KEY'
),
));
$response = curl_exec($curl);
curl_close($curl);
print_r($response);NodeJS
const request = require('request');
const options = {
'method': 'POST',
'url': 'https://dev.sellix.io/v1/payments',
'headers': {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
},
body: JSON.stringify({
"platform_on_behalf_of": "plat_149ffc-d0acb95171-sample",
"title": "Platform Payment",
"value": 2.5,
"currency": "USD",
"platform_fee": {
"percentage": 5,
"fixed_fee": 0.10
},
"email": "example@gmail.com",
"white_label": false,
"webhook": "https://webhook.sellix.io/api/v1/example",
"return_url": "https://sample.sellix.io/success"
})
};
request(options, function (error, response) {
if (error) throw new Error(error);
console.log(response.body);
});Python
import requests
import json
url = "https://dev.sellix.io/v1/payments"
payload = json.dumps({
"platform_on_behalf_of": "plat_149ffc-d0acb95171-sample",
"title": "Platform Payment",
"value": 2.5,
"currency": "USD",
"platform_fee": {
"percentage": 5,
"fixed_fee": 0.10
},
"email": "example@gmail.com",
"white_label": False,
"webhook": "https://webhook.sellix.io/api/v1/example",
"return_url": "https://sample.sellix.io/success"
})
headers = {
'Content-Type': 'application/json',
'Authorization': 'Bearer YOUR_API_KEY'
}
response = requests.request("POST", url, headers=headers, data=payload)
print(response.text)Concordium needs a different setup flow because it requires the merchant to perform a KYC process.
- the wallet prompts the merchant to insert his mnemonic
- the iframe location is redirected to the Concordium KYC provider page (without leaving the platform page)
- the merchant performs the KYC process
- the iframe location is redirected back to the wallet
- the merchant is asked again to insert his mnemonic and his account will be created
- the address is uploaded to sellix backend using the
/wallet/addressendpoint
flowchart TD
A[Merchant presses button to add concordium] --> B
B[Merchant is prompted to insert his mnemonic] --> C
C[Wallet is redirected to the KYC provider's page] --> D
D[Merchant fill's in his infos completing the KYC flow] --> E
E[Merchant is redirected back to the wallet] --> F
F[Merchant inserts his mnemonic again] --> G
G[Merchant concordium account is finalized] --> H
H[Merchant obtains his new concordium address] --> I
I[Address is uploaded to Sellix backend using /wallet/address]
Listed down here are the endpoints that Sellix backend uses to interact with the platform
- the platform calls Sellix API to create a new merchant
- the endpoint will require all the needed user information to create a new merchant:
{
email: "merchant@merchant.com", // unique
name: "merchantName", // unique
custom_fields: {} // object or null, the platform can pass additional information that can be later retrieved using GET /wallet/merchant
}- the endpoint returns:
{
status: 200,
data: {
merchant: {
id: 'plat_....',
name: 'merchantName',
email: 'merchant@merchant.com',
custom_fields: null,
token: 'platform_token_....',
expire_at: 00000000
}
},
error: null,
message: null,
env: 'production'
}- the platform calls Sellix API to retrive the
merchant_tokenthat will be used to authenticate the merchant during ALL the other wallet flows listed above. - the endpoint will require the
merchant_idas parameter - the endpoint returns:
{
status: 200,
data: {
merchant: {
id: 'plat_....',
name: 'merchantName',
email: 'merchant@merchant.com',
custom_fields: null,
token: 'platform_token_....',
expire_at: 00000000
}
},
error: null,
message: null,
env: 'production'
}The setup flow consists in a few steps that the platform has to follow in order to make the setup of new users proceed correctly.
Firstly the platform needs to integrate in its user system a way to store the merchant_id returned from sellix.
When the platform's backend calls /wallet/merchant the user will be added to the Sellix backend, as the response you will get the merchant_token and the merchant_id with the additional information that you passed calling /wallet/merchant.
For example, in this phase you should add the merchant_id to the corresponding column in you User table in your database
Now the user needs to complete the wallet setup flow to create his crypto addresses. In order to do that, the Platform frontend has to render the iframe that will host the wallet. To use correctly the wallet iframe the platform needs to pass a few parameters in query string while rendering the wallet, here's an example in JavaScript
const qs = new URLSearchParams({
token, // required, the token got from /wallet/merchant, the one starting with "platform_token_..."
platform_id: encodeURIComponent("platform-demo"), // required, a string identifying the platform, you can use what you want, the end users will not see this value
setup_finish_link: "http://localhost:9998/login.html" // required, a url of your page used to redirect the user once the setup is completed
dark: true // non required, whether the wallet should be shown in dark mode or not, do not pass the flag or leave undefined for light mode
})<iframe
src="<wallet_url>/setup?<qs>"
sandbox="allow-scripts allow-same-origin allow-downloads allow-popups allow-top-navigation"
allow=""
referrerpolicy="unsafe-url"
>
</iframe>Once the user is setup he has access to all the rest of the wallet functionalities.
The plaform frontend makes a call to the backend that will get his merchant_token using GET /wallet/merchant/<merchant_id>.
Now the wallet iframe can be rendered using the token with the same parameters as above on the /balances page
<iframe
src="<wallet_url>/balances?<qs>"
sandbox="allow-scripts allow-same-origin allow-downloads allow-popups allow-top-navigation"
allow=""
referrerpolicy="unsafe-url"
>
</iframe>Concordium needs a different KYC process to create an account and get the merchant's address so to support Concordium for the platform users you will need to add an additional parameter in the query string (redirect_uri), when the users presses Configure to add Concordium, is brought to the KYC provider page, once the KYC is completed the user will be redirected back using this redirect_uri
const qs = new URLSearchParams({
token,
platform_id: encodeURIComponent("platform-demo"),
redirect_uri: "http://localhost:9998/complete-concordium-setup.html", // <-- this one
setup_finish_link: "http://localhost:9998/login.html"
})In this example we created a different html page to complete Concodium setup, you can do as you like, the only constrant is that the iframe on the complete concordium uri has to be rendered using these parameters:
const qs = new URLSearchParams({
token,
platform_id: encodeURIComponent("platform-demo"),
url: encodeURIComponent(window.location.toString().split("#")[1]), // <-- get's the code uri that will be address to your redirect uri, used to complete concordium setup
setup_finish_link: "http://localhost:9998/login.html"
})And the iframe should be rendered like this:
<iframe
src="<walletUrl>/complete-concordium-setup?<qs>"
width="780px"
height="605px"
sandbox="allow-forms allow-scripts allow-same-origin allow-downloads allow-popups allow-top-navigation"
allow=""
referrerpolicy="no-referrer"
>
</iframe>Here's the list of all the accepted query parameters that can be passed to the wallet iframe url
The merchant_token that starts with platform_token_... is used to authenticate the user while comunicating with sellix backend
A string containing the platform name that will be used to store correctly the merchant token in the user's Session Storage
A url pointing to a platform page, this will be used in various instances:
- when the user completes the setup of his addresses on the
/setuppage, the lastcompletebutton will redirect the user using this link - when the user performs the
forgot mnemonicflow and completes it, the lastcompletebutton will redirect the user using this link - when the user performs the
concordium setupflow, the lastcompletebutton will redirect the user using this link
If passed the only accepted value is true, it will render the iframe using dark color palette instead of light
If passed the users will be able to perform the concordium setup flow, else the users won't be able to use concordium at all.
This is a url pointing to a page in the platform frontend that will handle the redirect after the user completes the KYC process.
The url in the returning page will look like this if we pass http://localhost:9998/complete-concordium-setup.html as redirect_uri:
http://localhost:9998/complete-concordium-setup.html#code_uri=...
It is required when rendering the iframe in the /complete-concordium-setup page. You have to pass the string after the # character like in the example:
// ...
const url = encodeURIComponent(window.location.toString().split("#")[1])
// ...