# Joy and Tapcart

{% hint style="info" %}
This integration is available for: **Ultimate only**
{% endhint %}

### Overview

[Tapcart](https://tapcart.com) is a mobile app builder that lets Shopify merchants create a branded iOS and Android shopping app — no coding required. By integrating Joy Loyalty with Tapcart through a **Custom Block**, you can surface your loyalty program — points balance, rewards, VIP tiers, and referrals — directly inside your mobile app, giving customers a seamless way to earn and redeem on the go.

When a customer opens the loyalty block in your Tapcart app:

* **Logged-in customers** see their points balance, available rewards, and can redeem coupons — all applied directly to the in-app cart.
* **Guest visitors** see your loyalty program overview and are prompted to log in or sign up.

***

### How it works

The integration uses a **Tapcart Custom Block** — a small React component that loads the Joy Loyalty widget inside your mobile app. Here's what happens behind the scenes:

1. The block detects whether the customer is logged in via Tapcart's customer data.
2. If logged in, it securely authenticates the customer with Joy using an HMAC signature so their points and rewards load correctly.
3. The Joy widget opens automatically inside the block.
4. When a customer redeems a reward, the generated coupon is applied to the Tapcart cart automatically and the customer is taken to the cart screen.

***

### How to set up

{% stepper %}
{% step %}
**Get your Joy API credentials**

In your Shopify admin, go to **Apps → Joy: Loyalty Program → Settings → Developers**.

In the **Manage keys** card, click **Generate key** (if you haven't already) to create your credentials. Copy your **App ID** and **Secret key** — you'll need both in a later step.

{% hint style="warning" %}
Generating a new key invalidates any previously issued key. If you've already connected another integration (e.g., Shopney) using an existing key, that integration will stop working until you re-enter the new key.
{% endhint %}
{% endstep %}

{% step %}
**Create a Custom Block in Tapcart**

In your Tapcart dashboard, go to **Custom Blocks** and click **Create New**. Choose a **React** custom block. This opens the code editor where you'll paste the integration code.
{% endstep %}

{% step %}
**Paste the integration code**

Replace the default code in the editor with the following. Make sure to replace the placeholder values with your actual **App ID** and **Secret key** from Step 1.

```jsx
import * as React from "react"

const JOY_SHOP_ID = "YOUR_APP_ID"         // Replace with your App ID
const JOY_SECRET_KEY = "YOUR_SECRET_KEY"   // Replace with your Secret key
const JOY_SDK_URL = "https://static.joy.so/avada-joy.min.js"

async function computeHmac(key, data) {
    const enc = new TextEncoder()
    const cryptoKey = await crypto.subtle.importKey(
        'raw', enc.encode(key),
        { name: 'HMAC', hash: 'SHA-256' },
        false, ['sign']
    )
    const sig = await crypto.subtle.sign('HMAC', cryptoKey, enc.encode(data))
    return Array.from(new Uint8Array(sig))
        .map(b => b.toString(16).padStart(2, '0')).join('')
}

function injectJoySdk() {
    if (document.getElementById('avada-joy-sdk')) return
    const s = document.createElement('script')
    s.id = 'avada-joy-sdk'
    s.src = JOY_SDK_URL
    s.defer = true
    document.body.appendChild(s)
}

function openJoyWidget(retries = 10) {
    if (typeof window.avadaJoyTrigger === 'function') {
        window.avadaJoyTrigger('open')
    } else if (retries > 0) {
        setTimeout(() => openJoyWidget(retries - 1), 500)
    }
}

export default function JoyLoyaltyBlock({ useTapcart }) {
    const Tapcart = useTapcart()
    const customer = Tapcart?.variables?.customer

    React.useEffect(() => {
        const init = async () => {
            const customerId = customer?.id
                ?.replace('gid://shopify/Customer/', '') || null
            const email = customer?.email?.toLowerCase() || null

            window.AVADA_JOY = {
                shopId: JOY_SHOP_ID,
                hideLauncher: true,
                customer: customerId ? {
                    id: customerId,
                    email,
                    hash_version: 'v2',
                    hash_secret: await computeHmac(
                        JOY_SECRET_KEY,
                        `${customerId}-${email}-${JOY_SHOP_ID}`
                    )
                } : {}
            }

            window.addEventListener('joy:ready', () => {
                openJoyWidget()

                window.addEventListener('joy:applyCoupon', (e) => {
                    const couponCode = e?.detail?.couponCode
                    if (!couponCode) return
                    Tapcart.action(
                        "cart/discount/add",
                        { code: couponCode }
                    )
                    Tapcart.action("screen/open", {
                        destination: { type: "route", id: "cart" }
                    })
                })
            }, { once: true })

            injectJoySdk()
        }

        init()
    }, [customer?.id])

    return <div />
}
```

{% hint style="info" %}
**Where to find your credentials:** In Joy, go to **Settings → Developers → Manage keys**. Your **App ID** is the `JOY_SHOP_ID` value and **Secret key** is the `JOY_SECRET_KEY` value.
{% endhint %}
{% endstep %}

{% step %}
**Add the Custom Block to your app**

In the Tapcart dashboard, navigate to your app's screen editor. Drag and drop the Custom Block you just created into the desired location — for example, a dedicated **Loyalty** tab in your app's bottom navigation, or as a section within your app's homepage or account page.

Save and publish your changes.
{% endstep %}

{% step %}
**Preview and test**

Open the **Tapcart Preview** app on your phone to verify the integration:

1. **As a guest:** You should see the Joy loyalty program overview.
2. **As a logged-in customer:** You should see your points balance, available rewards, and be able to redeem coupons that get applied to your cart.
   {% endstep %}
   {% endstepper %}

***

### How coupon redemption works

When a customer redeems a reward in the Joy widget, the integration automatically:

1. Captures the generated coupon code via the `joy:applyCoupon` event.
2. Applies the coupon to the Tapcart cart using `cart/discount/add`.
3. Redirects the customer to the cart screen so they can proceed to checkout with the discount applied.

No manual coupon entry is needed — the entire flow is seamless for the customer.

***

### FAQ

**Which Joy plan is required for this integration?**\
The Tapcart integration requires the **Ultimate plan** since it uses the Joy JavaScript SDK with API credentials.

**Do I need to generate a new key for this?**\
No. Your App ID and Secret key can be reused across integrations. Only click **Generate key** again if your key is compromised or you need to revoke access — doing so will invalidate the old key.

**The widget is not loading in my Tapcart app — what should I check?**\
Make sure your **App ID** and **Secret key** are correctly pasted in the code (no extra spaces or quotes). Also verify that you're using the latest version of the Tapcart Preview app, as older versions may not support Custom Blocks properly.

**Can guests (non-logged-in users) see the loyalty program?**\
Yes. Guests will see your loyalty program overview. When a customer is not logged in, the widget shows the program details and prompts them to sign in to view their points and redeem rewards.

**Will coupons generated in Joy work in the Tapcart checkout?**\
Yes. The integration uses Tapcart's `cart/discount/add` action to apply Shopify discount codes, which work the same way at checkout as coupons applied on your web store.

***

### Need help?

Contact Joy support at <support@joy.so> or visit [help.joy.so](https://help.joy.so) for more documentation.
