<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Kadir Melih Can</title>
    <description>The latest articles on DEV Community by Kadir Melih Can (@kdrmlhcn).</description>
    <link>https://dev.to/kdrmlhcn</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2627491%2Ffc731f16-be62-4765-aa95-7658443b4cfe.png</url>
      <title>DEV Community: Kadir Melih Can</title>
      <link>https://dev.to/kdrmlhcn</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/kdrmlhcn"/>
    <language>en</language>
    <item>
      <title>Setting Up App Store In-App Purchase Event Notifications: A Complete Guide</title>
      <dc:creator>Kadir Melih Can</dc:creator>
      <pubDate>Sun, 29 Dec 2024 14:11:11 +0000</pubDate>
      <link>https://dev.to/kdrmlhcn/setting-up-app-store-in-app-purchase-event-notifications-a-complete-guide-34ip</link>
      <guid>https://dev.to/kdrmlhcn/setting-up-app-store-in-app-purchase-event-notifications-a-complete-guide-34ip</guid>
      <description>&lt;p&gt;Are you an iOS developer looking for a simple way to monitor your app's in-app purchases and subscriptions? In this guide, I'll show you how to set up a notification system that sends real-time App Store purchase events directly to your Discord, Slack and Telegram channels. Perfect for indie developers and small teams!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🚀 &lt;strong&gt;Get Started Quickly&lt;/strong&gt;: Check out the &lt;a href="https://github.com/kdrmlhcn/ios-iap-events-notification" rel="noopener noreferrer"&gt;GitHub repository&lt;/a&gt; for ready-to-deploy code and detailed setup instructions.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The repository includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Serverless function code for both Google Cloud Functions and Cloudflare Workers&lt;/li&gt;
&lt;li&gt;Configuration templates for Discord, Telegram, and Slack&lt;/li&gt;
&lt;li&gt;Example notification payloads and testing scripts&lt;/li&gt;
&lt;li&gt;Comprehensive documentation and troubleshooting guides&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Table of Contents 📑
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;What We'll Build 🛠️&lt;/li&gt;
&lt;li&gt;
Setting Up Your Notification Channel 📢

&lt;ul&gt;
&lt;li&gt;Discord Setup&lt;/li&gt;
&lt;li&gt;Telegram Setup&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Setting Up and Deploying to Google Cloud Function ☁️&lt;/li&gt;
&lt;li&gt;Setting Up and Deploying to Cloudflare Workers ⚡️&lt;/li&gt;
&lt;li&gt;
Connecting to App Store Connect 🔗

&lt;ul&gt;
&lt;li&gt;Using Adapty&lt;/li&gt;
&lt;li&gt;Using RevenueCat&lt;/li&gt;
&lt;li&gt;Direct Setup&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
What You'll Get 📱

&lt;ul&gt;
&lt;li&gt;Example Notifications&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
Advanced Features 🔧

&lt;ul&gt;
&lt;li&gt;Customizing Notifications&lt;/li&gt;
&lt;li&gt;Error Handling&lt;/li&gt;
&lt;li&gt;Timezone Support&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Troubleshooting 🔍&lt;/li&gt;
&lt;li&gt;Platform Comparison 🤔&lt;/li&gt;
&lt;li&gt;
Cost Considerations 💰

&lt;ul&gt;
&lt;li&gt;Free Tier Comparison&lt;/li&gt;
&lt;li&gt;Cost Examples&lt;/li&gt;
&lt;li&gt;Optimization Tips&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Security Best Practices 🔒&lt;/li&gt;
&lt;li&gt;
Technical Details and Examples 🔧

&lt;ul&gt;
&lt;li&gt;Example App Store Notification&lt;/li&gt;
&lt;li&gt;Understanding the Notification Fields&lt;/li&gt;
&lt;li&gt;Processing Best Practices&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Testing with cURL Examples 🧪&lt;/li&gt;
&lt;li&gt;What's Next 🚀&lt;/li&gt;
&lt;li&gt;Need Help 🤝&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="what-well-build-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What We'll Build 🛠️
&lt;/h2&gt;

&lt;p&gt;We'll create a serverless function that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Receives App Store Server-to-Server notifications&lt;/li&gt;
&lt;li&gt;Processes the purchase events&lt;/li&gt;
&lt;li&gt;Sends beautifully formatted notifications to your preferred platform&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="setting-up-notification-channel-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Setting Up Your Notification Channel 📢
&lt;/h2&gt;

&lt;h3&gt;
  
  
  For Discord:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Open your Discord server&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Launch Discord and open the server where you want to create the webhook.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Create a new Channel&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you don't have a channel already, create one by clicking on the "+" sign next to your server’s channel list.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyoyurpenua34ai9l4yn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Feyoyurpenua34ai9l4yn.png" alt="create a new channel" width="800" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Access Channel Settings&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Hover over the channel you want to create a webhook for. Click the gear icon (⚙️) to open Channel Settings.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymnc1qfzc64c4i4h5lk4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fymnc1qfzc64c4i4h5lk4.png" alt="channel settings" width="800" height="430"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Go to Integrations&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;In the left sidebar, scroll down and click on Integrations. Under the "Integrations" section, click on Create Webhook.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk1lkfl1lffmlthxkmzx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvk1lkfl1lffmlthxkmzx.png" alt="Integrations" width="800" height="338"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Configure Your Webhook&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Name your webhook and customize its settings if needed. Afterward, click Copy Webhook URL to save the URL for later use.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgv1z521y1cth160nxw45.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgv1z521y1cth160nxw45.png" alt="Copy webhook url" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  For Telegram:
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;1. Message &lt;a href="https://t.me/botfather" rel="noopener noreferrer"&gt;@BotFather&lt;/a&gt; on Telegram&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Open Telegram and search for &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt;. This is the official bot used to create and manage bots on Telegram.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Create a new bot with &lt;code&gt;/newbot&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Start a chat with &lt;a class="mentioned-user" href="https://dev.to/botfather"&gt;@botfather&lt;/a&gt; and type /newbot to begin the process of creating a new bot.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fysamrxpxd04swwx4b519.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fysamrxpxd04swwx4b519.png" alt="create a new bot" width="800" height="821"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Copy your bot token&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;After creating your bot, you’ll receive a token. Copy the token by clicking on the red highlighted area in the message from BotFather.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytcmugql0tixfqyh944a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fytcmugql0tixfqyh944a.png" alt="Telegram Bot Token" width="800" height="418"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Create a channel and add your bot as an admin&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;If you don’t already have a Telegram channel, create one by going to the Telegram app and selecting New Channel. Once the channel is created, add your bot as an admin so it can send and receive messages.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjo69c5z0z1t20v5nqr7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fhjo69c5z0z1t20v5nqr7.png" alt="New Channel" width="800" height="270"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgopcva7ztomddkgdr2s5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgopcva7ztomddkgdr2s5.png" alt="Channel Info" width="800" height="250"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtwd7f9wpq5jxetkgma9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvtwd7f9wpq5jxetkgma9.png" alt="Channel Administrators" width="800" height="537"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhnuo77g1vtdshqzyt54.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzhnuo77g1vtdshqzyt54.png" alt="Add Admin" width="800" height="421"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklblwkkkqh2i3lcnl0sv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fklblwkkkqh2i3lcnl0sv.png" alt="Add your bot as admin" width="800" height="566"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Get your channel ID&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Send a message to your channel first. Then forward that message to&lt;br&gt;
&lt;a href="https://t.me/userinfobot" rel="noopener noreferrer"&gt;@userinfobot&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdursyddp9ufccvth82z1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdursyddp9ufccvth82z1.png" alt="Channel Message" width="800" height="295"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Write userinfobot in search, select the bot and forward the message.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1go5a2jka0ok54etluhp.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1go5a2jka0ok54etluhp.png" alt="Forward message to userinfobot" width="730" height="206"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Check message that UserInfoBot's sent you. (Eg. Your chat id: "-1002328487593")&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8rihoainvkizwg7xtra.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fa8rihoainvkizwg7xtra.png" alt="Get Chat ID" width="800" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="setting-up-cloud-function-id"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up and Deploying to Google Cloud Function ☁️
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Visit &lt;a href="https://console.cloud.google.com" rel="noopener noreferrer"&gt;Google Cloud Console&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Create a &lt;a href="https://console.cloud.google.com/projectcreate" rel="noopener noreferrer"&gt;new project&lt;/a&gt; or select an existing one&lt;/li&gt;
&lt;li&gt;Go to &lt;a href="https://console.cloud.google.com/functions/list" rel="noopener noreferrer"&gt;Cloud Run Functions&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click "Create Function"&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboi1s5zp7sao4ne4mtuv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fboi1s5zp7sao4ne4mtuv.png" alt="Create Function" width="800" height="53"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you did not enable required APIs previously, you will see this modal. You should click Enable button. After enabled APIs, you will be redirected the creating new function page.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo1h2vwdnn0dre4v3ggy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmo1h2vwdnn0dre4v3ggy.png" alt="Enable APIs" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enter a name (1), choose HTTPS (2), select Allow unauthenticated invocations (3)&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fph4qduamt6yf6k76go3y.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fph4qduamt6yf6k76go3y.png" alt="Fill function page" width="800" height="932"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;After you created function you will see the Code Editor&lt;/li&gt;
&lt;li&gt;You will have 2 files: &lt;code&gt;index.js&lt;/code&gt; and &lt;code&gt;package.json&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Copy the following code into &lt;code&gt;package.json&lt;/code&gt;:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;iap-notifications&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;description&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;App Store server-to-server (S2S) notifications handler for in-app purchases&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;main&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;index.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@google-cloud/functions-framework&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^3.4.4&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;axios&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.7.9&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;base64url&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^3.0.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;country-iso-3-to-2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^1.1.1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;date-fns-tz&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^3.2.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;jsonwebtoken&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;^9.0.2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;functions-framework --target=main --port=8080&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deploy:gcf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gcloud functions deploy iap-notifications --gen2 --runtime=nodejs20 --region=us-central1 --memory=256MB --timeout=60s --min-instances=0 --max-instances=10 --trigger-http --allow-unauthenticated&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;deploy:cf&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;wrangler deploy&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;keywords&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;app-store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;server-to-server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;notifications&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;telegram-bot&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;discord-webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;slack-webhook&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;author&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Kadir Melih Can&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;license&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MIT&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;engines&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt;=20.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6czcss4hv0tqiv1j2bl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff6czcss4hv0tqiv1j2bl.png" alt="Package.json code" width="800" height="527"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Copy the &lt;a href="https://github.com/kdrmlhcn/ios-iap-events-notification/blob/master/google_cloud/index.js" rel="noopener noreferrer"&gt;source code&lt;/a&gt; into &lt;code&gt;index.js&lt;/code&gt; and configure your notification settings:
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;CONFIG&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;general&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;allowSandboxNotifications&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Europe/Istanbul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;dateFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dd.MM.yyyy HH:mm:ss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;telegram&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;botToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_BOT_TOKEN&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;chatId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_CHAT_ID&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;discord&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;webhookUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_DISCORD_WEBHOOK_URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;slack&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;webhookUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;YOUR_SLACK_WEBHOOK_URL&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;channel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;#app-store-notifications&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Do not forget to change Entry point field's value as main as in screenshot.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u9joeu751euxba9gstb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1u9joeu751euxba9gstb.png" alt="Index.js code" width="800" height="675"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Click "Deploy" and wait for the deployment to complete&lt;/li&gt;
&lt;li&gt;Copy your function's URL from the "Trigger" tab&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo24sjvrd0jf648z47sam.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo24sjvrd0jf648z47sam.png" alt="Trigger URL" width="800" height="99"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a id="setting-up-cloudflare-id"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Setting Up and Deploying to Cloudflare Workers ⚡️
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1.&lt;/strong&gt; Create a Cloudflare Account:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visit &lt;a href="https://dash.cloudflare.com" rel="noopener noreferrer"&gt;Cloudflare Dashboard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Sign up for a free account if you don't have one&lt;/li&gt;
&lt;li&gt;Verify your email address&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2.&lt;/strong&gt; Create a New Worker:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click on "Workers &amp;amp; Pages" in the left sidebar
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F26n3rwv0cz7u6k0wgoqz.png" alt="Workers &amp;amp; Pages" width="514" height="466"&gt;
&lt;/li&gt;
&lt;li&gt;Click "Create" button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fusv73omu6q16zx4zt61x.png" alt="Create Application" width="800" height="324"&gt;
&lt;/li&gt;
&lt;li&gt;Select "Create Worker"
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkbp0h8qvugpkxq1faon0.png" alt="Create Worker" width="800" height="340"&gt;
&lt;/li&gt;
&lt;li&gt;Choose a name for your worker and deploy (e.g., "iap-notifications")
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgeaepllnljnvduyfgu6t.png" alt="First deploy" width="800" height="496"&gt;
&lt;/li&gt;
&lt;li&gt;After deployed successfully click the "Edit Code" button
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F2efcf70ceoks2b5pqlmo.png" alt="Edit Code" width="800" height="54"&gt;
&lt;/li&gt;
&lt;li&gt;Paste code in &lt;a href="https://github.com/kdrmlhcn/ios-iap-events-notification/blob/master/cloudflare/worker.js" rel="noopener noreferrer"&gt;&lt;code&gt;worker.js&lt;/code&gt;&lt;/a&gt; file into the code editor.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmjh2ln7qtyns8dqsj39n.png" alt="Deployed" width="800" height="322"&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;3.&lt;/strong&gt; Save and Deploy:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Click "Save and deploy"&lt;/li&gt;
&lt;li&gt;Wait for deployment to complete&lt;/li&gt;
&lt;li&gt;Copy your worker's URL (format: &lt;code&gt;https://iap-notifications.your-username.workers.dev&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="connecting-to-app-store-id"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Connecting to App Store Connect 🔗
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Using Adapty:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://app.adapty.io" rel="noopener noreferrer"&gt;Adapty&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to App settings &amp;gt; Apps in the Adapty dashboard&lt;/li&gt;
&lt;li&gt;Click to the iOS SDK section, scroll to &lt;strong&gt;App Store server notifications&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Enter your the &lt;em&gt;Cloud Function or CloudFlare Worker's URL&lt;/em&gt; to URL for forwarding raw Apple events&lt;/li&gt;
&lt;li&gt;Click Save in the bottom left corner.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffkdpxq1bw8rbq2iq4cbt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffkdpxq1bw8rbq2iq4cbt.png" alt="Adapty" width="800" height="288"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Using RevenueCat:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Log in to &lt;a href="https://app.revenuecat.com/overview" rel="noopener noreferrer"&gt;RevenueCat&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Navigate to your iOS app under Project settings &amp;gt; Apps in the RevenueCat dashboard&lt;/li&gt;
&lt;li&gt;Scroll to the &lt;strong&gt;Apple Server to Server&lt;/strong&gt; notification settings section, and enter your the &lt;em&gt;Cloud Function or CloudFlare Worker URL&lt;/em&gt;  in &lt;strong&gt;Apple Server Notification Forwarding URL&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Click Save Changes in the top right corner.
&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fid0f7fiyvn0l26lwkb56.png" alt="RevenueCat URL" width="800" height="136"&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Direct App Store Connect Setup:
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;If you are using an internal solution, your own backend url is probably added here. Therefore, you can prepare an endpoint for your own backend and give it to the Google Cloud function or CloudFlare worker we prepared as the raw event arrives.&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open &lt;a href="https://appstoreconnect.apple.com" rel="noopener noreferrer"&gt;App Store Connect&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Select your app&lt;/li&gt;
&lt;li&gt;Go to App Information&lt;/li&gt;
&lt;li&gt;Scroll to "App Store Server Notifications"&lt;/li&gt;
&lt;li&gt;Enter your Cloud Function or CloudFlare URL&lt;/li&gt;
&lt;li&gt;Save changes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="what-youll-get-id"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What You'll Get 📱
&lt;/h2&gt;

&lt;p&gt;Once set up, you'll receive beautifully formatted notifications for events like:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New purchases&lt;/li&gt;
&lt;li&gt;Subscription renewals&lt;/li&gt;
&lt;li&gt;Refunds&lt;/li&gt;
&lt;li&gt;Consumption requests&lt;/li&gt;
&lt;li&gt;Billing issues&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Example Notifications by Platform
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Telegram Notification
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo64039l0w0byn9isnn4.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqo64039l0w0byn9isnn4.jpg" alt="Telegram Notification Example 1" width="800" height="164"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvefhelpkujerlyqw4b0.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuvefhelpkujerlyqw4b0.PNG" alt="Telegram Notification Example 2" width="800" height="1734"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;MyApp.bundle.id
🔔 DID_RENEW $99.99 💵

📊 Event: AUTO_RENEW_ENABLED
🏷 Product: premium_yearly
🌍 Country: 🇺🇸 USA
💰 Price: 99.99 USD

ℹ️ Additional Info:
• Environment: Production
• ID: 2000000000000000
• Type: Auto-Renewable Subscription
• Expires Date: 15.03.2025 14:30:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Discord Notification
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifu48ssnwgw2az32d62a.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fifu48ssnwgw2az32d62a.jpg" alt="Discord Notification 1" width="800" height="422"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc5qqpfwws2gjt40vgrp.PNG" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frc5qqpfwws2gjt40vgrp.PNG" alt="Discord Notification 2" width="800" height="1734"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Discord notifications use rich embeds with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Color-coded status&lt;/li&gt;
&lt;li&gt;App Store icon thumbnail&lt;/li&gt;
&lt;li&gt;Clickable app link&lt;/li&gt;
&lt;li&gt;Organized fields for event details&lt;/li&gt;
&lt;li&gt;Footer with transaction details&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a id="advanced-features-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Advanced Features 🔧
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Customizing Notifications
&lt;/h3&gt;

&lt;p&gt;You can customize the notification format by modifying the respective service classes in &lt;code&gt;index.js&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;TelegramNotification&lt;/code&gt; for Telegram&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;DiscordNotification&lt;/code&gt; for Discord&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SlackNotification&lt;/code&gt; for Slack&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Error Handling
&lt;/h3&gt;

&lt;p&gt;The system includes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Automatic retries for failed API calls&lt;/li&gt;
&lt;li&gt;Rate limiting protection&lt;/li&gt;
&lt;li&gt;Sandbox environment filtering&lt;/li&gt;
&lt;li&gt;Detailed error logging&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Timezone Support
&lt;/h3&gt;

&lt;p&gt;Configure your preferred timezone in the CONFIG object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;general&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Your/Timezone&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;dateFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dd.MM.yyyy HH:mm:ss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Common Timezone Examples:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// United States&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;America/New_York&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;     &lt;span class="c1"&gt;// Eastern Time&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;America/Chicago&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;// Central Time&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;America/Denver&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;       &lt;span class="c1"&gt;// Mountain Time&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;America/Los_Angeles&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;// Pacific Time&lt;/span&gt;

&lt;span class="c1"&gt;// Europe&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Europe/London&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;        &lt;span class="c1"&gt;// British Time&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Europe/Paris&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;         &lt;span class="c1"&gt;// Central European Time&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Europe/Istanbul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;// Turkish Time&lt;/span&gt;

&lt;span class="c1"&gt;// Asia&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Dubai&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;          &lt;span class="c1"&gt;// Gulf Time&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Singapore&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;// Singapore Time&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Asia/Tokyo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;          &lt;span class="c1"&gt;// Japan Time&lt;/span&gt;

&lt;span class="c1"&gt;// Australia&lt;/span&gt;
&lt;span class="nx"&gt;timezone&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Australia/Sydney&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;    &lt;span class="c1"&gt;// Australian Eastern Time&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Date Format Examples:
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Common Formats&lt;/span&gt;
&lt;span class="nx"&gt;dateFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dd.MM.yyyy HH:mm:ss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;    &lt;span class="c1"&gt;// 31.12.2024 14:30:00&lt;/span&gt;
&lt;span class="nx"&gt;dateFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;MM/dd/yyyy hh:mm:ss a&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;  &lt;span class="c1"&gt;// 12/31/2024 02:30:00 PM&lt;/span&gt;
&lt;span class="nx"&gt;dateFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;yyyy-MM-dd HH:mm:ss&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;    &lt;span class="c1"&gt;// 2024-12-31 14:30:00&lt;/span&gt;
&lt;span class="nx"&gt;dateFormat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dd MMM yyyy HH:mm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;      &lt;span class="c1"&gt;// 31 Dec 2024 14:30&lt;/span&gt;

&lt;span class="c1"&gt;// Format Tokens&lt;/span&gt;
&lt;span class="c1"&gt;// dd    - Day of month (01-31)&lt;/span&gt;
&lt;span class="c1"&gt;// MM    - Month (01-12)&lt;/span&gt;
&lt;span class="c1"&gt;// yyyy  - Full year&lt;/span&gt;
&lt;span class="c1"&gt;// HH    - Hours in 24h format (00-23)&lt;/span&gt;
&lt;span class="c1"&gt;// hh    - Hours in 12h format (01-12)&lt;/span&gt;
&lt;span class="c1"&gt;// mm    - Minutes (00-59)&lt;/span&gt;
&lt;span class="c1"&gt;// ss    - Seconds (00-59)&lt;/span&gt;
&lt;span class="c1"&gt;// a     - AM/PM marker&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="troubleshooting-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting 🔍
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;No notifications received:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Check if your webhook URLs are correct&lt;/li&gt;
&lt;li&gt;Verify your Cloud Function is deployed and accessible&lt;/li&gt;
&lt;li&gt;Ensure the correct URL is set in App Store Connect&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Webhook errors:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Verify your bot/webhook permissions&lt;/li&gt;
&lt;li&gt;Check if the channel/chat IDs are correct&lt;/li&gt;
&lt;li&gt;Ensure your notification service is enabled in CONFIG&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Invalid timestamps:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Update the timezone in CONFIG to match your location&lt;/li&gt;
&lt;li&gt;Verify the dateFormat string is correct&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="platform-comparison-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Comparison of Google Cloud Functions vs Cloudflare Workers: Free vs Paid Tiers 🤔
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzhsjwyl4yfts5jj9h5w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkzhsjwyl4yfts5jj9h5w.png" alt="Platform comparison" width="800" height="512"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Which One Should You Choose?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Choose Google Cloud Functions if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Need more free tier requests&lt;/li&gt;
&lt;li&gt;Want deeper integration with other Google services&lt;/li&gt;
&lt;li&gt;Require more computing resources&lt;/li&gt;
&lt;li&gt;Need detailed logging and monitoring&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Choose Cloudflare Workers if you:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want global edge deployment&lt;/li&gt;
&lt;li&gt;Need better cold start performance&lt;/li&gt;
&lt;li&gt;Prefer simpler deployment process&lt;/li&gt;
&lt;li&gt;Want built-in DDoS protection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Both platforms are excellent choices for this project. For most indie developers, either option will work great as both offer generous free tiers and reliable performance.&lt;/p&gt;

&lt;p&gt;&lt;a id="cost-considerations-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Cost Considerations 💰
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Google Cloud Functions Free Tier
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;2 million invocations per month&lt;/li&gt;
&lt;li&gt;400,000 GB-seconds of compute time&lt;/li&gt;
&lt;li&gt;200,000 GHz-seconds of compute time&lt;/li&gt;
&lt;li&gt;5GB network egress per month&lt;/li&gt;
&lt;li&gt;No credit card required to start&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Paid Tier (if you exceed free tier):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$0.40 per million invocations&lt;/li&gt;
&lt;li&gt;$0.0000025 per GB-second&lt;/li&gt;
&lt;li&gt;$0.0000100 per GHz-second&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cloudflare Workers Free Tier
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;100,000 requests per day (~3 million/month)&lt;/li&gt;
&lt;li&gt;Unlimited scripts/workers&lt;/li&gt;
&lt;li&gt;128MB memory limit per worker&lt;/li&gt;
&lt;li&gt;10ms CPU time per request&lt;/li&gt;
&lt;li&gt;Workers KV: 100,000 reads per day&lt;/li&gt;
&lt;li&gt;Global edge deployment included&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Paid Tier (Workers Paid Plan):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;$5/month for 10 million requests&lt;/li&gt;
&lt;li&gt;Increased CPU limits (50ms)&lt;/li&gt;
&lt;li&gt;1GB memory limit per worker&lt;/li&gt;
&lt;li&gt;Workers KV: 1 million reads per day&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost Estimation Examples
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6ov4cqx2xm437gay273.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp6ov4cqx2xm437gay273.png" alt="Cost Estimation Examples" width="800" height="297"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;For a typical indie app (5,000 monthly active users):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Average 1-2 events per user per month&lt;/li&gt;
&lt;li&gt;Total: ~10,000 events/month&lt;/li&gt;
&lt;li&gt;Easily covered by both platforms' free tiers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For a growing app (50,000 monthly active users):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Average 1-2 events per user per month&lt;/li&gt;
&lt;li&gt;Total: ~100,000 events/month&lt;/li&gt;
&lt;li&gt;Google Cloud Functions: Still within free tier&lt;/li&gt;
&lt;li&gt;Cloudflare Workers: May need paid plan depending on daily distribution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;For a large app (500,000 monthly active users):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Average 1-2 events per user per month&lt;/li&gt;
&lt;li&gt;Total: ~1,000,000 events/month&lt;/li&gt;
&lt;li&gt;Google Cloud Functions: 

&lt;ul&gt;
&lt;li&gt;Still within free tier&lt;/li&gt;
&lt;li&gt;Minimal cost if exceeded (~$0.40)&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Cloudflare Workers:

&lt;ul&gt;
&lt;li&gt;Paid plan recommended ($5/month)&lt;/li&gt;
&lt;li&gt;Better global performance&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  Cost Optimization Tips
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Monitor Usage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Set up billing alerts&lt;/li&gt;
&lt;li&gt;Track daily/monthly usage&lt;/li&gt;
&lt;li&gt;Monitor error rates&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Reduce Costs&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Filter unnecessary notifications&lt;/li&gt;
&lt;li&gt;Batch events when possible&lt;/li&gt;
&lt;li&gt;Optimize code execution time&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Choose the Right Plan&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Start with free tier&lt;/li&gt;
&lt;li&gt;Upgrade only when needed&lt;/li&gt;
&lt;li&gt;Consider your app's growth rate&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Both platforms offer excellent value for money, especially for indie developers and small teams. The free tiers are typically sufficient for apps with up to 100,000 monthly active users, making them perfect for getting started and scaling up.&lt;/p&gt;

&lt;p&gt;&lt;a id="security-best-practices-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Security Best Practices 🔒
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Keep your webhook URLs private&lt;/li&gt;
&lt;li&gt;Enable Cloud Function authentication if needed&lt;/li&gt;
&lt;li&gt;Regularly rotate webhook URLs&lt;/li&gt;
&lt;li&gt;Monitor function logs for unusual activity&lt;/li&gt;
&lt;li&gt;Set up billing alerts in Google Cloud&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="technical-details-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Details and Examples 🔧
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Example App Store Notification
&lt;/h3&gt;

&lt;p&gt;Here's an example of the raw JSON data you'll receive from Apple (with randomized sensitive information):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"appAppleId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;9876543210&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bundleId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"bundleVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"renewalInfo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoRenewProductId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp_premium_annual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"autoRenewStatus"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"originalTransactionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200001234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp_premium_annual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"recentSubscriptionStartDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1729488392000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"renewalDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1761283592000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"renewalPrice"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;99990&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"signedDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1735414243588&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"transactionInfo"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"bundleId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"com.example.myapp"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"currency"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USD"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"expiresDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1761283592000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"inAppOwnershipType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"PURCHASED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"originalTransactionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200001234567890"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"price"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;99990&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"productId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"myapp_premium_annual"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"purchaseDate"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1729747592000&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"storefront"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"USA"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"storefrontId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"143441"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"transactionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"200009876543210"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Auto-Renewable Subscription"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"notificationType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"DID_CHANGE_RENEWAL_STATUS"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"subtype"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"AUTO_RENEW_ENABLED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2.0"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Understanding the Notification Fields
&lt;/h3&gt;

&lt;p&gt;Key fields in the notification:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Top Level&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;notificationType&lt;/code&gt;: The main event type (e.g., DID_CHANGE_RENEWAL_STATUS)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;subtype&lt;/code&gt;: Specific event subtype (e.g., AUTO_RENEW_ENABLED)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;version&lt;/code&gt;: API version (currently 2.0)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Renewal Info&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;autoRenewProductId&lt;/code&gt;: Product identifier for the subscription&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;autoRenewStatus&lt;/code&gt;: Current auto-renewal status (1 = enabled, 0 = disabled)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;renewalDate&lt;/code&gt;: Next renewal date in milliseconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;renewalPrice&lt;/code&gt;: Price in minor units (e.g., 99990 = $99.99)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Transaction Info&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;inAppOwnershipType&lt;/code&gt;: Purchase type (PURCHASED, FAMILY_SHARED, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;originalTransactionId&lt;/code&gt;: Original purchase identifier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transactionId&lt;/code&gt;: Current transaction identifier&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;purchaseDate&lt;/code&gt;: Purchase timestamp in milliseconds&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;expiresDate&lt;/code&gt;: Subscription expiration timestamp&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Processing Best Practices
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Validation&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Always verify the notification's signature&lt;/li&gt;
&lt;li&gt;Check environment (Production vs. Sandbox)&lt;/li&gt;
&lt;li&gt;Validate bundle ID matches your app&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Error Handling&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement exponential backoff for retries&lt;/li&gt;
&lt;li&gt;Log failed notification processing&lt;/li&gt;
&lt;li&gt;Set up alerts for repeated failures&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Data Storage&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Store raw notifications for debugging&lt;/li&gt;
&lt;li&gt;Index by transactionId and originalTransactionId&lt;/li&gt;
&lt;li&gt;Keep audit logs of processed notifications&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  cURL Examples App Store Notification
&lt;/h3&gt;

&lt;p&gt;Here's an example of the raw JSON data you'll receive from Apple (with randomized sensitive information):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"signedPayload"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQ1Njc4OTAsImJ1bmRsZUlkIjoiY29tLmx1bmFyLmZpdG5lc3Nwcm8iLCJidW5kbGVWZXJzaW9uIjoiMS4wIiwiZW52aXJvbm1lbnQiOiJQcm9kdWN0aW9uIiwidHJhbnNhY3Rpb25JbmZvIjp7ImJ1bmRsZUlkIjoiY29tLmx1bmFyLmZpdG5lc3Nwcm8iLCJjdXJyZW5jeSI6IktSVyIsImVudmlyb25tZW50IjoiUHJvZHVjdGlvbiIsImV4cGlyZXNEYXRlIjoxNzk5OTk5OTk5MDAwLCJpbkFwcE93bmVyc2hpcFR5cGUiOiJQVVJDSEFTRUQiLCJvcmlnaW5hbFRyYW5zYWN0aW9uSWQiOiI0ODAwMDIwNTc0ODk0ODMiLCJwcmljZSI6MjkwMDAsInByb2R1Y3RJZCI6ImZpdG5lc3Nwcm9fcHJlbWl1bV95ZWFybHkiLCJwdXJjaGFzZURhdGUiOjE3MDM4MDY1MjAwMDAsInN0b3JlZnJvbnQiOiJLT1IiLCJ0eXBlIjoiQXV0by1SZW5ld2FibGUgU3Vic2NyaXB0aW9uIn19LCJub3RpZmljYXRpb25UeXBlIjoiRElEX0NIQU5HRV9SRU5FV0FMX1BSRUYiLCJzdWJ0eXBlIjoiVVBHUkFERSIsInZlcnNpb24iOiIyLjAifQ.secret"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a id="testing-with-curl-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing with CURL Examples 🧪
&lt;/h3&gt;

&lt;p&gt;Here are some example CURL commands to test your notification system with different scenarios. Replace &lt;code&gt;YOUR_FUNCTION_URL&lt;/code&gt; with your actual Cloud Function or Cloudflare Worker URL.&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Subscription Renewal (Korean Market)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"YOUR_FUNCTION_URL"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "signedPayload": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQ1Njc4OTAsImJ1bmRsZUlkIjoiY29tLmx1bmFyLmZpdG5lc3Nwcm8iLCJidW5kbGVWZXJzaW9uIjoiMS4wIiwiZW52aXJvbm1lbnQiOiJQcm9kdWN0aW9uIiwidHJhbnNhY3Rpb25JbmZvIjp7ImJ1bmRsZUlkIjoiY29tLmx1bmFyLmZpdG5lc3Nwcm8iLCJjdXJyZW5jeSI6IktSVyIsImVudmlyb25tZW50IjoiUHJvZHVjdGlvbiIsImV4cGlyZXNEYXRlIjoxNzk5OTk5OTk5MDAwLCJpbkFwcE93bmVyc2hpcFR5cGUiOiJQVVJDSEFTRUQiLCJvcmlnaW5hbFRyYW5zYWN0aW9uSWQiOiI0ODAwMDIwNTc0ODk0ODMiLCJwcmljZSI6MjkwMDAsInByb2R1Y3RJZCI6ImZpdG5lc3Nwcm9fcHJlbWl1bV95ZWFybHkiLCJwdXJjaGFzZURhdGUiOjE3MDM4MDY1MjAwMDAsInN0b3JlZnJvbnQiOiJLT1IiLCJ0eXBlIjoiQXV0by1SZW5ld2FibGUgU3Vic2NyaXB0aW9uIn19LCJub3RpZmljYXRpb25UeXBlIjoiRElEX0NIQU5HRV9SRU5FV0FMX1BSRUYiLCJzdWJ0eXBlIjoiVVBHUkFERSIsInZlcnNpb24iOiIyLjAifQ.secret"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  2. Subscription Refund (German Market)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"YOUR_FUNCTION_URL"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "signedPayload": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQ1Njc4OTAsImJ1bmRsZUlkIjoiY29tLnplbml0aC5tZWRpdGF0ZSIsImJ1bmRsZVZlcnNpb24iOiIxLjAiLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJ0cmFuc2FjdGlvbkluZm8iOnsiYnVuZGxlSWQiOiJjb20uemVuaXRoLm1lZGl0YXRlIiwiY3VycmVuY3kiOiJFVVIiLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJleHBpcmVzRGF0ZSI6MTc5OTk5OTk5OTAwMCwiaW5BcHBPd25lcnNoaXBUeXBlIjoiUFVSQ0hBU0VEIiwib3JpZ2luYWxUcmFuc2FjdGlvbklkIjoiMzcwMDAxODU4MzM3NzcxIiwicHJpY2UiOjE0OTksInByb2R1Y3RJZCI6Im1lZGl0YXRlX3VubGltaXRlZF95ZWFybHkiLCJwdXJjaGFzZURhdGUiOjE3MDM4MjI2NTkwMDAsInN0b3JlZnJvbnQiOiJERVUiLCJ0eXBlIjoiQXV0by1SZW5ld2FibGUgU3Vic2NyaXB0aW9uIn19LCJub3RpZmljYXRpb25UeXBlIjoiUkVGVU5EIiwic3VidHlwZSI6bnVsbCwidmVyc2lvbiI6IjIuMCJ9.secret"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Subscription Renewal (Brazilian Market)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"YOUR_FUNCTION_URL"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{
  "signedPayload": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJkYXRhIjp7ImFwcEFwcGxlSWQiOjEyMzQ1Njc4OTAsImJ1bmRsZUlkIjoiY29tLnNreXdhdmUucGhvdG9tYWdpYyIsImJ1bmRsZVZlcnNpb24iOiIxLjAiLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJ0cmFuc2FjdGlvbkluZm8iOnsiYnVuZGxlSWQiOiJjb20uc2t5d2F2ZS5waG90b21hZ2ljIiwiY3VycmVuY3kiOiJCUkwiLCJlbnZpcm9ubWVudCI6IlByb2R1Y3Rpb24iLCJleHBpcmVzRGF0ZSI6MTc5OTk5OTk5OTAwMCwiaW5BcHBPd25lcnNoaXBUeXBlIjoiUFVSQ0hBU0VEIiwib3JpZ2luYWxUcmFuc2FjdGlvbklkIjoiNDEwMDAyMjYzNTMxNjA2IiwicHJpY2UiOjY5OTAsInByb2R1Y3RJZCI6InBob3RvbWFnaWNfcHJvX2FubnVhbF90aWVyMSIsInB1cmNoYXNlRGF0ZSI6MTcwMzg3MjYwOTAwMCwic3RvcmVmcm9udCI6IkJSQSIsInR5cGUiOiJBdXRvLVJlbmV3YWJsZSBTdWJzY3JpcHRpb24ifX0sIm5vdGlmaWNhdGlvblR5cGUiOiJESURfUkVORVciLCJzdWJ0eXBlIjpudWxsLCJ2ZXJzaW9uIjoiMi4wIn0.secret"
}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These examples demonstrate different notification types (renewal, refund, upgrade) across various markets with different currencies. The payloads are base64-encoded JWTs that will be decoded by the notification handler. You can use these to test your notification system's handling of:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Different Currencies &amp;amp; Regions&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Korean Won (KRW)&lt;/li&gt;
&lt;li&gt;Euro (EUR)&lt;/li&gt;
&lt;li&gt;Brazilian Real (BRL)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Various Notification Types&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DID_CHANGE_RENEWAL_PREF with UPGRADE subtype&lt;/li&gt;
&lt;li&gt;REFUND&lt;/li&gt;
&lt;li&gt;DID_RENEW&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Price Formats&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Whole numbers (29 KRW)&lt;/li&gt;
&lt;li&gt;Decimal prices (6.99 BRL)&lt;/li&gt;
&lt;li&gt;Four-digit prices (14.99 EUR)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To use these examples:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Replace &lt;code&gt;YOUR_FUNCTION_URL&lt;/code&gt; with your actual endpoint URL&lt;/li&gt;
&lt;li&gt;Run the CURL command in your terminal&lt;/li&gt;
&lt;li&gt;Check your configured notification channels (Discord/Telegram/Slack) for the formatted notification&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a id="whats-next-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next 🚀
&lt;/h2&gt;

&lt;p&gt;Now that you have real-time purchase notifications set up, you can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Monitor your app's revenue in real-time&lt;/li&gt;
&lt;li&gt;Quickly respond to refund requests&lt;/li&gt;
&lt;li&gt;Track subscription patterns&lt;/li&gt;
&lt;li&gt;Identify potential issues early&lt;/li&gt;
&lt;li&gt;Make data-driven decisions for your app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember to star the repository if you found it helpful! ⭐️&lt;/p&gt;

&lt;p&gt;&lt;a id="need-help-id"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Need Help 🤝
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Create an issue on &lt;a href="https://github.com/kdrmlhcn/ios-iap-events-notification/issues" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Text me on &lt;a href="https://t.me/kdrmlhcn" rel="noopener noreferrer"&gt;Telegram&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Acknowledgments 🙏
&lt;/h2&gt;

&lt;p&gt;Special thanks to &lt;a href="https://github.com/ramazanarslan" rel="noopener noreferrer"&gt;Ramazan Arslan&lt;/a&gt; for his initial implementation that inspired this project. I started by using his published version and then further developed it to make it simpler and more accessible for everyone to implement their own App Store server notifications handler.&lt;/p&gt;

&lt;p&gt;This project is built with these amazing open-source packages:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/GoogleCloudPlatform/functions-framework-nodejs" rel="noopener noreferrer"&gt;@google-cloud/functions-framework&lt;/a&gt; - For serverless function development&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/axios/axios" rel="noopener noreferrer"&gt;Axios&lt;/a&gt; - For HTTP requests&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/brianloveswords/base64url" rel="noopener noreferrer"&gt;base64url&lt;/a&gt; - For URL-safe base64 encoding&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/vtex/country-iso-3-to-2" rel="noopener noreferrer"&gt;country-iso-3-to-2&lt;/a&gt; - For country code conversion&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/auth0/node-jsonwebtoken" rel="noopener noreferrer"&gt;jsonwebtoken&lt;/a&gt; - For JWT handling&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/marnusw/date-fns-tz" rel="noopener noreferrer"&gt;date-fns-tz&lt;/a&gt; - For timezone-aware date formatting&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Happy coding! 🎉&lt;/p&gt;

</description>
      <category>ios</category>
      <category>serverless</category>
      <category>swift</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
