FiMs Pay is a fully customisable web app, that can be use as a Point Of Sale by a merchant, or directly as a Customer Payment app (with the option to pay privately). The app requires pre-registered merchants, but can also be used on-the-fly by directly entering one's merchant infos (shop's name, Solana wallet address, currency accepted and maximum value). It has many options available to configure the app to suit your needs.
Use the code as a reference, run it directly from FiMs website (see below) or build it yourself to start accepting decentralized payments in-person.
- Usable all over the world, translated in many languages: English, Spanish, Portuguese, French, Japanese and Esperanto
- Accept a lot of currencies: agEUR, Euro, USDC, USDT, SOL and even BONK
- Send Private payments with Elusiv
- Fully responsive web app with themes: classic, color or black & white
- Register your shop's info for fast access or just test it immediately
- Choose your favorite RPC
- Compatible with Mobile Wallet Adapter and "Saga" mobile phone
- Use Transfer / Transaction Request via QR code
- Fully Configurable in a settings file or in a Google Spreadsheet (on the fly)
- Custom Faucet / Onramp link and about page / website
And much more ...
This Point of Sale use the @solana/pay
JavaScript library to create a simple point of sale system, yet usable in a shop!
The merchant use the Point of Sale on its device (tablet / computer / phone) and set the amount to receive.
The customer will then scan a QR code and validate the transaction on its phone.
- Check out the app, inspired by Solana's Proof of Concept version.
- Try a customised version
- Watch a real life video
If the merchant does not have a point of sale and just want to receive payments, it would have to redirect its customers to the customer direct payment web app on its mobile phone:
- Try paying directly here
- Check out a quick demo
- Watch a real life video
Responsive (PAY) | Desktop (POS) |
---|---|
To build and run this app locally, you'll need:
-
Node.js v18 or above
-
NPM / Yarn
-
Setup two wallets on Solflare or Phantom (Merchant and Customer)
Follow the guide (Solflare) or guide (Phantom) on how to create a wallet. This wallet will provide the recipient address.
Follow the guide (Solflare) or guide (Phantom) on how to create another wallet. This wallet will be paying for the goods/services.
- Click the settings icon in the Phantom window
- Under the
General > Network
setting, selectDevnet
- Click the settings icon in the Phantom window
- Select the
Change network
option and selectDevnet
Use solfaucet or solfaucet to airdrop SOL to the customer wallet and merchant wallet. Alternatively, if you need USDC_Dev (a dev version of USDC), use this SPL token faucet.
You'll need SOL in the customer wallet to pay for the goods/services + transaction fees. You'll also need SOL in the merchant wallet to indicate that this wallet is active. If you are using a SPL token (any token different than SOL), you'll need to first create a SPL token account in the merchant wallet.
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
git clone https://github.com/flodef/FiMs-Pay.git
gh repo clone flodef/FiMs-Pay
cd FiMs-Pay/
npm install
Make a copy of the setting example in order to set your own:
cp .env.example .env.local
To run the program in dev, change at least the setting and set it to NEXT_PUBLIC_IS_DEV=true
A complete list of the available settings is available at the end of the page.
npm run dev
npm run proxy
open "https://localhost:3001"
You may need to accept a locally signed SSL certificate to open the page.
Alternatively, you can go straight to the point of sale, by adding at least one of the following info:
- a merchant address (recipient): should be a valid solana wallet address
- a store name (label): can be any name
- a currency (currency): should be one of the accepted currency (currently USDC, USDT, agEUR, EUR, SOL and BONK)
- a max value (maxValue): can be any number You can specify a parameter by adding it at the end of the web address.
Notes
- Between the web address and the first parameter, a
?
should be added.- Between a parameter and its value, a
=
should be added.- Between the two parameters, a
&
should be added.- Because space character is not allowed in a web address, replace all spaces by a
+
.
Example:
open "https://localhost:3001?recipient=Your+Merchant+Address&label=Your+Store+Name¤cy=BONK&maxValue=2"
Find and download the SPL Token icon as a .svg
Put the file in the client/images
folder.
Open the file as a text and copy the svg code to create a Function Component.
To do so, go into the client/components/images
folder.
If you don't feel inspired to create one on your own, just make a copy of SOLIcon.tsx
, rename it to your SPL Token name, and modify the following lines:
export const SOLIcon: FC<SVGProps<SVGSVGElement>> = ({ width = 32, height = 32 }) => {
return (
<svg
width={width}
height={height}
fill="none"
viewBox="0 0 84 84"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
>
Change
SOLIcon
to your SPL Token name (eg: BONKIcon for $BONK). Replace the Function Component<svg></svg>
by your own. Make sure thewidth
andheight
are accepting JSX parameters (should be between brackets{ }
).
Find your token info on a Solana Explorer, for example Solscan:
- Token name
- Token Symbol (the label written between parenthesis next to Token Name)
- Token address
- Decimals
Add your token info in the client/utils/constants.ts
file:
- Import your created Function Component Icon created in the previous step (replace 'TOKEN_NAME' by your Token name):
import { TOKEN_NAMEIcon } from '../components/images/TOKEN_NAMEIcon';
- Create a Public Key from the Token address:
const MAINNET_TOKEN_NAME_MINT = new PublicKey('Token_Address');
- Add your token to the currency list:
export const CURRENCY_LIST: currencyType =
process.env.NEXT_PUBLIC_IS_DEV === 'true'
? {
SOL: { splToken: undefined, icon: SOLIcon, decimals: 9, minDecimals: 1, symbol: '◎' },
USDC_Dev: { splToken: DEVNET_DUMMY_MINT, icon: USDCIcon, decimals: 6, minDecimals: 2, symbol: 'USD' },
}
: {
SOL: { splToken: undefined, icon: SOLIcon, decimals: 9, minDecimals: 1, symbol: '◎' },
//...
BONK: {
splToken: MAINNET_BONK_MINT,
icon: BONKIcon,
decimals: 5,
minDecimals: 2,
symbol: 'BONK',
multiplier: Multiplier.M,
},
};
The parameters should be filled like this:
- splToken: the Public Key created in step 2,
- icon: the Icon imported in step 1,
- decimals: the decimals number retrieved on the Solana Explorer,
- minDecimals: the minimum number of decimals needed,
- symbol: the token symbol retrieved on the Solana Explorer,
- multiplier: a multiplier in case the currency has a very small / high valuation price (for example, if 1 Million $BONK = $1, it can make sense to use a Million high multiplier to avoid typing too much 0. Reversely, if 1 $Bitcoin = $100 000, you can use a small multiplier like using Satoshi conversion). Multiplier is optional and can be found in the
client/utils/multiplier.ts
file.
Once done, you should see your new added token in the home page Token list. It also can be directly selected by adding the currency=TokenName
parameter in the web address.
Transaction Requests are a new feature in Solana Pay.
In the settings file (.env.local), set NEXT_PUBLIC_USE_LINK=true
The generated QR codes in the app should now use transaction requests. To see what's going on and customize it, check out the server/api/index.ts
file.
You can deploy this point of sale app to Vercel with a few clicks.
Fork the Solana Pay repository
Login to Vercel and create a new project
Import the forked repository from GitHub.
If you're forked repository is not listed, you'll need to adjust your GitHub app permissions. Search for the app permission in the project's repository settings.
Configure the project as follows:
Notes At this step, you can set up manually any settings in
Environment Variables
that are in your.env.local
file. However, I would recommend to wait for the build to deploy and follow the next step...
To import settings, go to Settings
then Environment Variables
.
Settings in .env.local
file can be imported one by one, or in one go.
To copy one setting at the time, copy a setting line and paste it directly the Key
text box (for example NEXT_PUBLIC_IS_DEV=true
). It will automatically copy the parameter properly.
If you want to import the whole .env.local
file, imports all your settings in one go by clicking on Import
then selecting the file and finally click on Save
.
Notes
- You can click
select custom branch
if you want your settings to only apply to a specific branch.Production
is set to your master branch (the branch by defaults).- Once a setting is changed, you'll have to
Redeploy
your project.
Once the deployment finishes, enjoy by navigating to:
https://<YOUR DEPLOYMENT URL>
Here is the list of all availables settings with their explanation and default value (if not set):
- NEXT_PUBLIC_CLUSTER_ENDPOINT=https://solana-mainnet.rpc.extrnode.com : The RPC address to the server that will execute all the request to the Solana network.
- RATE_LIMIT=10 : Number of RPC requests that can be sent during a specific interval (RATE_LIMIT_INTERVAL).
- RATE_LIMIT_INTERVAL=60 : Number of seconds to define the interval during which a number of requests can be sent (RATE_LIMIT).
- IMAGE_DOMAIN=flodef.github.io : Domain Address where images are stored (used by Next.JS to cache and improve loading performances).
- NEXT_PUBLIC_CURRENCY=SOL : Default currency used (Possible value can be : EUR, agEUR, USDC, USDC_Dev, USDT, SOL, BONK).
- NEXT_PUBLIC_MAX_VALUE=99999.99 : Default maximum value a merchant can receive as a payment (for example, a bakery won't probably never be paid more than $1000).
- NEXT_PUBLIC_SHOW_SYMBOL=false : Whether to show the currency's symbol or its name (for example, SOL currency's name is SOL and symbol's ◎, as well USDC's symbol would be $).
- NEXT_PUBLIC_IS_DEV=false : Whether the Solana Network used is Devnet or Mainnet .
- NEXT_PUBLIC_USE_HTTP=false : Whether the Web protocol used is HTTP or HTTPS (in dev, HTTPS needs a proxy to run in background while HTTP do not).
- NEXT_PUBLIC_USE_LINK=false : Whether to use Transaction Requests (true) or Transfer Requests (false).
- NEXT_PUBLIC_USE_WEB_WALLET=false : Whether to connect to a Web wallet first or use Mobile Wallet Adapter.
- NEXT_PUBLIC_USE_CUSTOM_CRYPTO=false : Whether to use a custom cryptographic algorithm (slower but more secure) or an external library (faster but less secure)
- NEXT_PUBLIC_DEFAULT_WALLET : The default wallet to connect to (for example, if you are sure that all of your customers are only using one wallet). Leave empty for user to choose among different wallets.
- NEXT_PUBLIC_APP_TITLE=FiMs Pay : The app default title, that will be displayed in the web page and use as default merchant label.
- NEXT_PUBLIC_DEFAULT_LANGUAGE=en : The app default language if the browser language is not found as a translated language (currently, english, esperanto, spanish, french, japanese and portuguese).
- NEXT_PUBLIC_DEFAULT_THEME=color : The app default theme (Possible value can be: classic, color, blackWhite)
- NEXT_PUBLIC_MERCHANT_IMAGE_PATH='/Img/Merchant/' : The path or Web address to where the merchant's logo is located
- NEXT_PUBLIC_ABOUT_LINK=https://fims.fi/pay : A web address to know more about the app / project
- NEXT_PUBLIC_HELP_LINK=https://fims.fi/faq : A web address to find help about the app / project
- NEXT_PUBLIC_FAUCET=https://spl-token-faucet.com : A web address to a SOL / SPL Token faucet
- NEXT_PUBLIC_FAUCET_ADDRESS : The faucet's Solana public key
- NEXT_PUBLIC_FAUCET_SUPPLY_AMOUNT=1 : The initial amount provided by the faucet
- NEXT_PUBLIC_PRIVATE_PAYMENT : Whether to use Private payments or not
These are your personnal Private Keys. You can use them in your private .env.local
(on your own machine) and in Vercel Environment settings (encrypted).
DO NOT SHARE THEM OR USE THEM ANYWHERE ELSE !!!
- NEXT_PUBLIC_GOOGLE_SPREADSHEET_ID : The Google Spreadsheet ID to load data from (if not specified, data will be loaded from the
server/data
folder). - NEXT_PUBLIC_GOOGLE_API_KEY : The Google API key allowing to connect to a Spreadsheet.
- NEXT_PUBLIC_CRYPTO_SECRET : The crypto secret key used to cipher the FiMs wallet private key
- NEXT_PUBLIC_FAUCET_ENCODED_KEY : The faucet wallet private key, encoded with the crypto secret key above
Private Payments are provided by Elusiv. Elusiv is a privacy solution using ZK technology with an off-chain “Warden Network” to create private transactions. You can read more about it on their website: https://elusiv.io/
In order to use Elusiv ZK private payments, you'll be asked to sign a message with your Solana wallet.
With Private Payments, you need to first Top Up your private funds from your wallet. In order to stay private and to avoid your payment being traced to your account, all your funds in your wallet will be transfered to your private balance.
Once your private funds has enough balance, you can then proceed to your payment as usual (enter the amount and click "Pay").
When your payment is complete, you can keep your private funds for further payments or withdraw all the left balance back to your wallet.
Notes
- Currently only working in localhost because Elusiv Warden Network is based on HTTP requests which the browser blocks
- Elusiv is only available on devnet currently - our app interacts with devnet
If you want to use the app for a single merchant, I would recommend to use a web address link, accessible from the mobile / desktop device as a shortcut. For example, go to https://pos.fims.fi?currency=USDC&maxValue=99999.99&recipient=5uXKZWCYbaKwFMwu9BxMGEYowbhuG34qqYkg36hL5YvG&label=FiMs+Pay💕 Then change the label and recipient parameters with your own. Finally, create a shortcut to access it in one click.
If you have multiple shops / multiple locations, you can simply edit the server/data/merchant.json
file :
- index : an unique id number to identify the merchant
- address : the Solana wallet address of the merchant
- company : the merchant's / shop's name
- currency : the currency used
- maxValue : the maximum value that can be received with one payment
- location : the merchant's / shop's location
{
"values": [
["index", "address", "company", "currency", "maxValue", "location"],
[2, "GZLPJYkR3diD8c8vpgb7sXHEH9sG1R9sWa3yPwaL9Peb", "Le pain d'Annette", "EUR", 300, "Saint-Nic"]
// ...
]
}
Notes Don't delete the first row
["index", "address", "company", "currency", "maxValue", "location"],
as it's mandatory.
JSon file is pretty strait forward to edit. However, it's not flexible because everytime you want to add / delete / modify a merchant, you have to commit to github and deploy a new version of your project on Vercel. To avoid it, you can use an online spreadsheet containing all the merchant's information. Any modification will be live updated in the app.
- Go to https://drive.google.com/ and create an account if you don't have one.
- Add a New spreadsheet:
+ New > Google Sheets > Blank Spreadsheet
- Change the spreedsheet's sheet name from
Sheet1
tomerchant
- Insert the merchant's info (first row is mandatory):
index address company currency maxValue location
2 GZLPJYkR3diD8c8vpgb7sXHEH9sG1R9sWa3yPwaL9Peb Le pain d'Annette EUR 300 Saint-Nic
- Share the Spreadsheet by going to
File > Share > Share with others
then change the share settings fromRestricted
toAnyone with the link
and clickDone
- Copy the Spreadsheet GUID from the Spreadsheet web address (for example, in https://docs.google.com/spreadsheets/d/1uX6hJ3xa3ZuOzXrGYDR9XckoeAhjJXdAobZcs18VQw4/, the GUID is
1uX6hJ3xa3ZuOzXrGYDR9XckoeAhjJXdAobZcs18VQw4
) and Paste it in your.env.local
settings file and / or in Vercel's Environment Variables underNEXT_PUBLIC_GOOGLE_SPREADSHEET_ID
(for example,NEXT_PUBLIC_GOOGLE_SPREADSHEET_ID=MY_SPREADSHEET_ID
). - Go to https://console.cloud.google.com/
- Click on
Select a Project
thenNew Project
- In the Navigation Menu (top left button), go to
APIs & Services > Credentials
- Click on
+ CREATE CREDENTIALS
thenAPI Keys
- Copy your new created API key and Paste it in your
.env.local
settings file and / or in Vercel's Environment Variables underNEXT_PUBLIC_GOOGLE_API_KEY
(for example,NEXT_PUBLIC_GOOGLE_API_KEY=MY_API_KEY
).
Notes
- Never give your API Key to anybody untrusted as it can be used against you.
- To avoid this, you can restrict your key by clicking on it in the
APIs & Services
dashboard, then use Application Restrictions or API Restrictions (at least, tickGoogle Sheets API
) thenSave
- You should also never commit it on Github in
.env.example
(.env.local
file always stays on your device, and Vercel's Environment Variables are encrypted by default).
As of today, the app has been translated in english, esperanto, spanish, french, japanese and portuguese. That's already great, but we can do better...
Please, feel free to add your own language in the locales
folder.
To do so, copy the en
folder and rename it to your language own ISO 639-1 code 5.
Then use the json format provided, where you need to translate the right text part (for example, "merchants": "List of Merchants"
where merchants
is the immuable id and List of Merchants
is the translation).
I would be very happy to receive your translated texts and add it to the project. In that case, kindly send a Pull Request ;-)
The FiMs Pay Point of Sale app is open source and available under the Apache License, Version 2.0. See the LICENSE file for more info.