> For the complete documentation index, see [llms.txt](https://docs.thunkable.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.thunkable.com/~/changes/wHhkjLVeheBnfbpghRnw/in-app-purchases/host-your-server-side-verification-code-on-firebase/setup-your-cloud-environment/android-subscriptions-code.md).

# Android Subscriptions Code

These codes will allow you to acknowledge and verify purchases or subscriptions. There is 1 call for each type of transaction.&#x20;

### 1. To get started, go to[ console.google.cloud.com/functions](https://console.cloud.google.com/functions/list)

### 2. Click Create Function

<div align="left"><figure><img src="/files/a99O29gTZM4pyKbMUuFa" alt=""><figcaption></figcaption></figure></div>

### 3. Give your new function  a meaningful name

**androidSubscriptionHandler** should be the  name you use here

<div align="left"><figure><img src="/files/RUTqVu5cMNT3psl0feIr" alt=""><figcaption></figcaption></figure></div>

### 4. Save the trigger type

<div align="left"><figure><img src="/files/crHQTIAD5FAkBCMOhJuw" alt=""><figcaption></figcaption></figure></div>

### 5. Copy the code below and click the blue "next" button

<details>

<summary>Copy Me</summary>

{% code title="androiidSubscriptionHandler." %}

```
const functions = require("firebase-functions");
const admin = require("firebase-admin");
const googleServiceAccountKey = require("./serviceAccount.json");
admin.initializeApp({
  credential: admin.credential.cert(googleServiceAccountKey),
});
const {google} = require("googleapis");
const axios = require("axios");

exports.androidSubscriptionHandler = functions.https.onRequest((request, response) => {

  // caputre the data from the request
  const {purchaseObject, type} = request.body;
  functions.logger.info("trying to handle " + type + " for the follow purchaseObject");
  functions.logger.info(JSON.stringify(purchaseObject));
  // get the token and subscription id from the request
  const purchaseToken = purchaseObject.purchaseToken;
  const subscriptionID = purchaseObject.productId;
  // set your package id
  const packageID = "edu.fit.my.jgibb2018.pob";

  const returnTheResponse = (data) => {
    functions.logger.log("returning the response" + JSON.stringify(data));
    response.status(200).send(data);
  };

  const acknowledgeSubscription = (err, tokens) => {
    functions.logger.info(`
    Attempting to acknowledge subscription ${subscriptionID}\n
    your access token for a manual retry ${tokens.access_token}\n
    your purchase object ${JSON.stringify(purchaseObject)}\n\n
    `);

    const config = {
      method: "post",
      url: `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${packageID}/purchases/subscriptions/${subscriptionID}/tokens/${purchaseToken}:acknowledge`,
      headers: {
        "Authorization": `Bearer ${tokens.access_token}`,
      },
    };
    functions.logger.info("acknowledge config" + JSON.stringify(config));
    axios(config)
        .then(function(r) {
          returnTheResponse("Your transaction has been completed");
        })
        .catch(function(e) {
          functions.logger.warn("an error occured while acknowledging the subscription");

          functions.logger.error(JSON.stringify({error: e}));

          returnTheResponse({error: e.data, status: e.status, message: e.message});
        });
  };

  const verifySubscription = (err, tokens) => {
    functions.logger.info("verify function");
    const config = {
      method: "get",
      url: `https://androidpublisher.googleapis.com/androidpublisher/v3/applications/${packageID}/purchases/subscriptions/${subscriptionID}/tokens/${purchaseToken}`,
      headers: {
        "Authorization": `Bearer ${tokens.access_token}`,
      },
    };
    functions.logger.info(config.url);
    axios(config)
        .then(function(r) {
          functions.logger.info("verify success" + JSON.stringify(r.data));
          returnTheResponse(r.data);
        })
        .catch(function(e) {
          returnTheResponse(JSON.stringify({error: e.data, status: e.status, message: e.message}));
        });
  };

  const getAccessToken = () => {
    const jwtClient = new google.auth.JWT(
        googleServiceAccountKey.client_email,
        null,
        googleServiceAccountKey.private_key,
        ["https://www.googleapis.com/auth/androidpublisher"],
        null,
    );
    try {
      if (type == "subscriptionAcknowledge") {
        jwtClient.authorize(acknowledgeSubscription);
      } else if (type == "subscriptionVerify") {
        jwtClient.authorize(verifySubscription);
      }
    } catch (error) {
      functions.logger.error(error);
      response.status(500).send("issue getting getting auth", JSON.stringify(error));
    }
  };

  getAccessToken();
  });
```

{% endcode %}

</details>

<div align="left"><figure><img src="/files/uuKo1qX48hqEpsBlE1Lg" alt=""><figcaption></figcaption></figure></div>

### 6. Clear the default and paste the copied code into the code

{% embed url="<https://vimeo.com/698771346>" %}

### 7. Update the package.json file

<details>

<summary>copy and paste over the default text</summary>

```
{
  "name": "sample-http",
  "version": "0.0.1",
  "dependencies": {
     "axios": "^0.26.0",
     "googleapis": "^97.0.0",
     "firebase-functions": "^3.18.0",
     "firebase-admin": "^10.0.2"
   }
}

```

</details>

{% embed url="<https://player.vimeo.com/video/698770935>" %}

### 8. Set the name of the functions entry point

the name used here should match the name used in **Step 3** above

### 9. Click the plus icon to create a new file and name it "serviceAccount.json"

<div><figure><img src="/files/5KGWBweSiOQyg6DVwEF7" alt=""><figcaption></figcaption></figure> <figure><img src="/files/apBtRqgJg89SlwyPx8FI" alt=""><figcaption></figcaption></figure></div>

### 10. Copy and paste the content of this projects [service account](broken://spaces/KrMxDvAEx21XNB81zaoj/pages/QktBJZFZ3K0BNAgz4nBN#download_your_service_key) file from your computer into this newly created file.&#x20;

<div align="left"><figure><img src="/files/jlYFsOb0dvrVV3gNIfdM" alt=""><figcaption></figcaption></figure></div>

### 11. Deploy the function

<div align="left"><figure><img src="/files/tqOeYkzX0ljdyd80j7Gg" alt=""><figcaption></figcaption></figure></div>

### 12. Set function invocation privacy permissions

This allows any device with your endpoint URL the ability to verify purchases made via your app

1. Go to <https://console.cloud.google.com/functions/list>
2. Select the function you want to make public. (the function you just created)
3. Click the **Permissions** tab.
4. Click **Add Principle**
5. In the **Add members** field, type `allUsers`
6. Select the **Cloud Function Invoker** role from the **Select a role** drop-down menu.
7. Click **Add**.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.thunkable.com/~/changes/wHhkjLVeheBnfbpghRnw/in-app-purchases/host-your-server-side-verification-code-on-firebase/setup-your-cloud-environment/android-subscriptions-code.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
