Hands-On Example: Build a Working Prototype in 10 min
If you just want to get something working quickly and understand how the integration flows, follow these 10 steps to implement a minimal Ealyx integration. This guide assumes you're integrating Ealyx Pay. If you're using Other Payment Methods instead, skip from Step 6 directly to Step 9.
Prerequisites
- Sandbox credentials from Ealyx (CLIENT_ID, CLIENT_SECRET, USERNAME, PASSWORD)
- curl installed on your system
- A basic backend endpoint that can handle HTTP requests
- jq installed (for JSON parsing in examples)
Integration Steps
Step 1: Set Your Credentials
Create a file: ealyx-config.sh
export CLIENT_ID="your_client_id_here"
export CLIENT_SECRET="your_client_secret_here"
export USERNAME="your_username_here"
export PASSWORD="your_password_here"
export API_BASE_URL="https://api.stg.ealyx.tech"
export ASSETS_URL="https://cdn.stg.ealyx.tech"
Load the config
source ealyx-config.sh
Step 2: Authenticate and Get Tokens
# Authenticate
BASIC_AUTH=$(echo -n "$CLIENT_ID:$CLIENT_SECRET" | base64 | tr -d '\n')
AUTH_RESPONSE=$(curl -s -X POST "$API_BASE_URL/oauth2/token/" \
-H "Authorization: Basic $BASIC_AUTH" \
-H 'Content-Type: application/x-www-form-urlencoded' \
-d 'grant_type=password' \
-d "username=$USERNAME" \
-d "password=$PASSWORD" \
-d "scope=core payments valuations")
# Extract tokens
export ACCESS_TOKEN=$(echo $AUTH_RESPONSE | jq -r '.access_token')
export REFRESH_TOKEN=$(echo $AUTH_RESPONSE | jq -r '.refresh_token')
export USER_ID=$(echo $AUTH_RESPONSE | jq -r '.user_id')
export MERCHANT_ID=$(echo $AUTH_RESPONSE | jq -r '.merchant_id')
echo "✓ Authentication successful"
echo "Access Token: ${ACCESS_TOKEN:0:20}..."
echo "User ID: $USER_ID"
echo "Merchant ID: $MERCHANT_ID"
| Check | Expected | If it fails |
|---|---|---|
ACCESS_TOKEN | Not null | Check CLIENT_ID/SECRET are correct |
USER_ID | Not null | Check USERNAME/PASSWORD are correct |
MERCHANT_ID | Not null | Contact Ealyx - credentials may not be activated |
Save these values: USER_ID and MERCHANT_ID are needed later for webhook signature validation.
Step 3: Add Teasers to Your Frontend
Add this to your HTML (before </body>):
<script>
window.ealyxConfiguration = {
merchantId: '{MERCHANT_ID}',
browserCallbackBase: '{MERCHANT_CALLBACKS_BASE}',
confirmUrl: '{MERCHANT_SUCCESS_URL}',
products: 'valuations+ep1' // Default is just 'valuations'
};
import('{ASSETS_URL}/v1/main.js')
.then(module => { window.Ealyx = module.Ealyx; })
.catch(err => console.error('Error loading Ealyx:', err));
</script>
<!-- Add teasers where you want them -->
<div class="ealyx-teaser" data-type="product-widget"></div>
Open browser DevTools (F12) → Console tab:
| Check | Expected | If it fails |
|---|---|---|
| No errors in console | Clean load | Check {ASSETS_URL} is correct |
window.Ealyx exists | Object with methods | SDK failed to load - check network tab |
| Teaser appears | Widget visible on page | Check data-type attribute is valid |
Step 4: Implement shopper_data Endpoint
Create a GET endpoint at $MERCHANT_CALLBACKS_BASE/shopper_data:
See Implement Shopper Data Endpoint for complete implementation details.
Test your endpoint:
curl -X GET "$MERCHANT_CALLBACKS_BASE/shopper_data" \
-H 'Content-Type: application/json' \
-b 'session=YOUR_SESSION_COOKIE'
Expected response (when user is logged in):
{
"customer": {
"id": "customer_123",
"email": "john@example.com",
"given_name": "John",
"family_name": "Doe"
},
"session_hash_token": "abc123def456..."
}
| Check | Expected | If it fails |
|---|---|---|
| HTTP status | 200 OK | Check endpoint URL and method (must be GET) |
customer object | Present with id, email, names | Ensure user is logged in when testing |
session_hash_token | String (HMAC hash) | Check your HMAC-SHA256 implementation |
Step 5: Implement purchase_data Endpoint
Create a GET endpoint at $MERCHANT_CALLBACKS_BASE/purchase_data:
See Implement Purchase Data Endpoint for complete implementation details.
Test your endpoint:
curl -X GET "$MERCHANT_CALLBACKS_BASE/purchase_data" \
-H 'Content-Type: application/json' \
-b 'session=YOUR_SESSION_COOKIE'
Expected response (when cart has items):
{
"purchase": {
"cart": {
"id": "cart_123",
"currency": "EUR",
"items": [{"id": "SKU1", "name": "Phone", "quantity": 1, "price": 10000, "tax": 2100, "total": 12100}],
"total": 12100
},
"billing": {"given_name": "John", "family_name": "Doe", "...": "..."},
"shipping": {"address": {"...": "..."}, "method": {"...": "..."}},
"customer": {"id": "customer_123", "email": "john@example.com"}
}
}
| Check | Expected | If it fails |
|---|---|---|
| HTTP status | 200 OK | Check endpoint URL and method (must be GET) |
purchase.cart.items | Array with cart items | Add items to cart before testing |
| All amounts are integers | 12100 not 121.00 | Multiply by 100 (values in cents) |
| Empty cart | {"purchase": null} | This is correct behavior |
Step 6: Add Ealyx Pay Option to Checkout
First, call window.Ealyx?.paymentOptionDetailsFor('ep1') to determine how to display the Ealyx Pay option:
const details = window.Ealyx?.paymentOptionDetailsFor('ep1');
// Returns: { mode, template_name, label, info, extra?, data? }
mode | Meaning | Action |
|---|---|---|
normal | Trade-in ready for EP1 | Show as selectable payment method |
popup | No valuation yet | Show option that opens valuation wizard when selected |
disable | EP1 not available | Show as disabled with explanation |
The response includes localized text fields:
label: Primary text for the payment method (e.g., "Pay €114.00 less with Ealyx Pay")info: Explanatory text for info popups/tooltipsextra: Secondary text for expanded payment method details (optional)
Note: The label is plain text but the info and extra fields may contain HTML tags, like <br> for line breaks, <strong> for bold text, etc. They will never be long or complex.
In your checkout page HTML:
<label>
<input type="radio" name="payment_method" data-module-name="ealyx" value="ealyx_pay" />
<img src="img/ealyx-pay.svg" />
<span>{label}</span>
<span style="display: none;">{extra}</span>
<span style="display: none;">{info}</span>
</label>
If mode is popup, you need to open the valuation wizard (window.Ealyx.openValuation()) when the shopper tries to select the option. Refresh the payment option details when the valuation wizard is closed (listen for the ealyx-valuation-updated event), and select the Ealyx Pay option if and only if mode is normal.
Notes:
- It is mandatory to include an EalyxPay icon somewhere in the payment option. It provides necessary context for some label texts.
- The
data-module-name="ealyx"attribute is used to identify the Ealyx Pay option to the SDK. - The
infoandextrafields start out hidden. Theinfofield is designed to be displayed in a tooltip when the shopper hovers over the payment option or in a sidebar when the shopper clicks on an ℹ️ icon, and theextrafield is designed to be displayed inline when the shopper selects the payment option. Neither field is mandatory, but most shops will want to use at leastinfo.
Step 7: Add JavaScript Listeners for Ealyx Pay
// Show/hide Ealyx Pay option details
const paymentRadios = document.querySelectorAll('input[name="payment_method"]');
paymentRadios.forEach(radio => {
radio.addEventListener("change", () => {
const isEalyx = radio.value === "ealyx_pay";
window.Ealyx?.updatePaymentSelected(isEalyx);
});
});
// Intercept form submission to open Ealyx modal instead
document.querySelector("form").addEventListener("submit", function (e) {
const selected = document.querySelector('input[name="payment_method"]:checked');
const isEalyx = selected?.value === "ealyx_pay";
if (isEalyx) {
e.preventDefault();
window.Ealyx?.openPayment();
return false;
}
});
| Check | Expected | If it fails |
|---|---|---|
| Ealyx Pay radio visible | Shows in payment options | Check HTML is in checkout page |
| Selecting Ealyx Pay | No JS errors | Check window.Ealyx exists |
| Clicking "Place Order" | Ealyx modal opens | Check form submit listener is attached |
| Modal shows discount | Trade-in value visible | Complete valuation first (Steps 3-4) |
Step 8: Implement Order Webhook (POST /order)
Create a POST endpoint at $MERCHANT_WEBHOOK_BASE/order:
See Handle Order Webhook for complete implementation details.
Test signature validation locally:
SECRET="$USER_ID-$MERCHANT_ID" # NOT CLIENT_SECRET!
BODY='{"data":{"cart_id":"cart_123","status":"pending"}}'
EXPECTED=$(echo -n "$BODY" | openssl dgst -sha256 -hmac "$SECRET" | cut -d' ' -f2)
echo "Expected signature: $EXPECTED"
| Check | Expected | If it fails |
|---|---|---|
| Endpoint accessible | HTTP 200 (not 404) | Check URL is publicly accessible |
| Signature matches | Computed = received | Secret must be USER_ID-MERCHANT_ID, NOT CLIENT_SECRET |
| Response code | HTTP 200 exactly | Ealyx requires 200 (not 201, 204, etc.) |
| Order created | Record in database | Check your order creation logic |
Step 9: Test the Flow
# 1. Visit your site and click "Calculate Discount"
# 2. Complete the valuation in the modal
# 3. Select "Pay with Ealyx" at checkout
# 4. Complete payment
# 5. Check your logs for webhook call
# 6. Verify order status in your database
echo "✓ Ealyx Pay integration complete!"
Step 10: Go to Production
Once testing is complete, request production credentials from your Ealyx account manager and update your configuration to use both the production endpoints and credentials:
export API_BASE_URL="https://api.ealyx.tech"
export ASSETS_URL="https://cdn.ealyx.tech"
# Use your production credentials
export CLIENT_ID="your_production_client_id"
export CLIENT_SECRET="your_production_client_secret"
export USERNAME="your_production_username"
export PASSWORD="your_production_password"