Receive payments with Mavis Pay
Overview
This guide describes how to integrate Mavis Pay into your project. The integration process consists of the following steps:
- Send a payment request to the Mavis Pay API.
- Set up a webhook URL to receive real-time updates whenever a payment status changes.
- Verify the authenticity of incoming webhooks.
Prerequisites
- API key for request authentication. To get your API key, create and set up an app in the Developer Console.
- Access to the Mavis Pay service. To request, speak to your Sky Mavis point of contact.
Base URLs
Both private and public API hosts are available. The private API calls require your API key, while the public calls do not.
Environment | Call type | Host |
---|---|---|
Production | Private (requires API key) | https://api-gateway.skymavis.com/mavispay/v1 |
Production | Public (no API key required) | https://api-gateway.skymavis.com/mavispay-public/v1 |
Development | Private (requires API key) | https://api-gateway.skymavis.one/mavispay/v1 |
Development | Public (no API key required) | https://api-gateway.skymavis.one/mavispay-public/v1 |
Authentication
To authenticate your Mavis Pay API requests, include your API key in the X-API-Key
header field with each request.
Steps
Step 1. Create a payment request
To create a new payment request, send information to this API endpoint:
POST /merchant/payments
Request body:
{
"amount": "27500000",
"currency": "usdc",
"items": [
{
"name": "AxieCon 2022 Backpack",
"quantity": 1
}
],
"reference": "55f56c89-b454-4677-a1a1-d3271f47db6f",
"redirect_url": "<https://localhost:3000/checkout>"
}
Fields
Field | Type | Description |
---|---|---|
amount | String | The full amount of currency to pay, without decimal places. USDC has 6 decimals places, so “27500000” equals “27.5” USDC. |
currency | String | The currency you as a merchant want to receive. Supported values: usdc , axs , weth , slp , ron . Note: You must enable the currency using a smart contract. |
items | Array | The items in the user's cart. An array of objects with name and quantity. |
items[index].name | String | The name of the item in the cart. |
items[index].quantity | String | The quantity of the item in the cart. |
reference | String | A payment reference generated by the merchant to later identify incoming webhook notifications. We suggest using UUID. |
reference_url | String | The URL where the Mavis Pay frontend redirects users after the payment is complete or expired. Must begin with https , even for testing. Note: Request your Sky Mavis point of contact to add this URL to your Sky Mavis Account. |
Response:
{
"ID": 1,
"CreatedAt": "0001-01-01T00:00:00Z",
"UpdatedAt": "0001-01-01T00:00:00Z",
"DeletedAt": null,
"payment_id": "99e9f183-e509-478b-8eda-02f6d38c57ab",
"merchant_id": "65e141ec-a735-47fe-8d57-fe514e03ddb6",
"amount": 27500000,
"currency": {
"Name": "USDC",
"Address": "0x067fbff8990c58ab90bae3c97241c5d736053f77"
},
"redirect_url": "<https://localhost:3000/checkout>",
"reference": "55f56c89-b454-4677-a1a1-d3271f47db6f",
"blockchain_reference": "1683256082984259400_40b20d1d-52dc-40cc-8767-14bdd54cf730",
"status": "created",
"transaction_id": "",
"items": [
{
"name": "AxieCon 2022 Backpack",
"quantity": 1
}
],
"expiresAt": "2023-05-05T05:13:02.9842594+02:00",
"createdAt": "2023-05-05T05:08:02.9853351+02:00",
"updatedAt": "2023-05-05T05:08:02.9853351+02:00"
}
After receiving the payment details from the API, redirect the customer to the following URL:
- Production:
https://mavispay.skymavis.com/pay?id=<payment_id>
- Development:
https://mavispay.skymavis.one/pay?id=<payment_id>
Append the value of payment_id
that returns from the create payment endpoint.
Payments expire automatically after 300 seconds (5 minutes). This is a global setting that can’t be changed for individual merchants or payments.
Step 2. Receive payment updates
To receive updates about your payments, set up a webhook receiver URL that can communicate over HTTPS.
Mavis Pay sends a POST request to this endpoint whenever a payment event occurs—for example, when a payment is created through the API or when it is completed.
Webhook events and payloads
The following table describes all payment events sent by Mavis Pay to your webhook URL.
Event | Sent when | Example payload |
---|---|---|
Payment created | A new payment is created through the API. | Payment created |
Payment expired | A payment is not completed within 5 minutes. | Payment expired |
Payment completed | A payment is completed and the funds are in the merchant's wallet. | Payment completed |
Payment failed | The transaction is included in a block but failed to execute. For example, ran out of gas. | Payment failed |
Payment created
{
"payment": {
"payment_id": "0459991c-c0a6-4819-b0bc-4aa7a9775f82",
"reference": "2c912268-4327-4bd7-b778-2632e3421ba5",
"status": "created"
}
}
Payment expired
{
"payment": {
"payment_id": "31830bbb-ee10-4970-bd50-18e1e272544b",
"reference": "4a9a29b8-4601-4b1a-8373-fdbca4a96018",
"status": "expired",
"transaction_hash": ""
}
}
Payment completed
{
"payment": {
"payment_id": "5d6feb44-d40a-44e2-8a1d-41b19f117e20",
"reference": "1dde7e08-4379-462d-949b-4e5cd9e373a4",
"status": "completed",
"transaction_hash": "0xf7d5c3ad0ad15a285d430503c2a798dd6103a80a5f1bd59f047952d1718bfd05"
}
}
Payment failed
{
"payment": {
"payment_id": "6eeef102-eb9a-459e-9b1f-6cc584181868",
"reference": "796387aa-ba2c-4bdb-8ec4-0e3d3cc83393",
"status": "failed",
"transaction_hash": "0x6233691e7459f7a057d1a2b2ae7997715b6ead0c7574cd8ebcfc8098c45dee4b"
}
}
Step 3. Verify a webhook
To verify the authenticity of a webhook request, follow these steps:
- Extract the webhook signature from the
x-signature
header of the request. - Verify that the signature is a JWT token (JSON Web Token) using the following public JWKS (JSON Web Key Set):
- Production:
https://api-gateway.skymavis.com/mavispay-public/.well-known/jwks.json
- Development:
https://api-gateway.skymavis.one/mavispay-public/.well-known/jwks.json
- Production:
This example verifies a webhook:
import jwt from 'jsonwebtoken';
import JwksRsa from 'jwks-rsa';
// Your JWT token
const signature = 'your_jwt_token_here';
// JWKS URL
const jwksUrl = 'your_jwks_url_here';
// Initialize JWKS client
const client = JwksRsa({
jwksUri: jwksUrl
});
// Function to get the signing key
function getKey(header: jwt.JwtHeader, callback: jwt.SigningKeyCallback) {
client.getSigningKey(header.kid, (err, key) => {
const signingKey = key.getPublicKey();
callback(null, signingKey);
});
}
// Verify the token
jwt.verify(signature, getKey, (err, decoded) => {
if (err) {
console.log('Token is invalid:', err);
} else {
console.log('Token is valid. Decoded payload:', decoded);
}
});