summaryrefslogtreecommitdiff
path: root/modules/inappstore
diff options
context:
space:
mode:
Diffstat (limited to 'modules/inappstore')
-rw-r--r--modules/inappstore/SCsub15
-rw-r--r--modules/inappstore/config.py6
-rw-r--r--modules/inappstore/in_app_store.h77
-rw-r--r--modules/inappstore/in_app_store.mm411
-rw-r--r--modules/inappstore/in_app_store_module.cpp48
-rw-r--r--modules/inappstore/in_app_store_module.h32
-rw-r--r--modules/inappstore/inappstore.gdip17
7 files changed, 606 insertions, 0 deletions
diff --git a/modules/inappstore/SCsub b/modules/inappstore/SCsub
new file mode 100644
index 0000000000..cee6a256d5
--- /dev/null
+++ b/modules/inappstore/SCsub
@@ -0,0 +1,15 @@
+#!/usr/bin/env python
+
+Import("env")
+Import("env_modules")
+
+env_inappstore = env_modules.Clone()
+
+# (iOS) Enable module support
+env_inappstore.Append(CCFLAGS=["-fmodules", "-fcxx-modules"])
+
+# (iOS) Build as separate static library
+modules_sources = []
+env_inappstore.add_source_files(modules_sources, "*.cpp")
+env_inappstore.add_source_files(modules_sources, "*.mm")
+mod_lib = env_modules.add_library("#bin/libgodot_inappstore_module" + env["LIBSUFFIX"], modules_sources)
diff --git a/modules/inappstore/config.py b/modules/inappstore/config.py
new file mode 100644
index 0000000000..e68603fc93
--- /dev/null
+++ b/modules/inappstore/config.py
@@ -0,0 +1,6 @@
+def can_build(env, platform):
+ return platform == "iphone"
+
+
+def configure(env):
+ pass
diff --git a/modules/inappstore/in_app_store.h b/modules/inappstore/in_app_store.h
new file mode 100644
index 0000000000..c8e5d17cec
--- /dev/null
+++ b/modules/inappstore/in_app_store.h
@@ -0,0 +1,77 @@
+/*************************************************************************/
+/* in_app_store.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef IN_APP_STORE_H
+#define IN_APP_STORE_H
+
+#include "core/object/class_db.h"
+
+#ifdef __OBJC__
+@class GodotProductsDelegate;
+@class GodotTransactionsObserver;
+
+typedef GodotProductsDelegate InAppStoreProductDelegate;
+typedef GodotTransactionsObserver InAppStoreTransactionObserver;
+#else
+typedef void InAppStoreProductDelegate;
+typedef void InAppStoreTransactionObserver;
+#endif
+
+class InAppStore : public Object {
+ GDCLASS(InAppStore, Object);
+
+ static InAppStore *instance;
+ static void _bind_methods();
+
+ List<Variant> pending_events;
+
+ InAppStoreProductDelegate *products_request_delegate;
+ InAppStoreTransactionObserver *transactions_observer;
+
+public:
+ Error request_product_info(Dictionary p_params);
+ Error restore_purchases();
+ Error purchase(Dictionary p_params);
+
+ int get_pending_event_count();
+ Variant pop_pending_event();
+ void finish_transaction(String product_id);
+ void set_auto_finish_transaction(bool b);
+
+ void _post_event(Variant p_event);
+ void _record_purchase(String product_id);
+
+ static InAppStore *get_singleton();
+
+ InAppStore();
+ ~InAppStore();
+};
+
+#endif
diff --git a/modules/inappstore/in_app_store.mm b/modules/inappstore/in_app_store.mm
new file mode 100644
index 0000000000..62977318c1
--- /dev/null
+++ b/modules/inappstore/in_app_store.mm
@@ -0,0 +1,411 @@
+/*************************************************************************/
+/* in_app_store.mm */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "in_app_store.h"
+
+#import <Foundation/Foundation.h>
+#import <StoreKit/StoreKit.h>
+
+InAppStore *InAppStore::instance = NULL;
+
+@interface SKProduct (LocalizedPrice)
+
+@property(nonatomic, readonly) NSString *localizedPrice;
+
+@end
+
+//----------------------------------//
+// SKProduct extension
+//----------------------------------//
+@implementation SKProduct (LocalizedPrice)
+
+- (NSString *)localizedPrice {
+ NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
+ [numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
+ [numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
+ [numberFormatter setLocale:self.priceLocale];
+ NSString *formattedString = [numberFormatter stringFromNumber:self.price];
+ return formattedString;
+}
+
+@end
+
+@interface GodotProductsDelegate : NSObject <SKProductsRequestDelegate>
+
+@property(nonatomic, strong) NSMutableArray *loadedProducts;
+@property(nonatomic, strong) NSMutableArray *pendingRequests;
+
+- (void)performRequestWithProductIDs:(NSSet *)productIDs;
+- (Error)purchaseProductWithProductID:(NSString *)productID;
+- (void)reset;
+
+@end
+
+@implementation GodotProductsDelegate
+
+- (instancetype)init {
+ self = [super init];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.loadedProducts = [NSMutableArray new];
+ self.pendingRequests = [NSMutableArray new];
+}
+
+- (void)performRequestWithProductIDs:(NSSet *)productIDs {
+ SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:productIDs];
+
+ request.delegate = self;
+ [self.pendingRequests addObject:request];
+ [request start];
+}
+
+- (Error)purchaseProductWithProductID:(NSString *)productID {
+ SKProduct *product = nil;
+
+ NSLog(@"searching for product!");
+
+ if (self.loadedProducts) {
+ for (SKProduct *storedProduct in self.loadedProducts) {
+ if ([storedProduct.productIdentifier isEqualToString:productID]) {
+ product = storedProduct;
+ break;
+ }
+ }
+ }
+
+ if (!product) {
+ return ERR_INVALID_PARAMETER;
+ }
+
+ NSLog(@"product found!");
+
+ SKPayment *payment = [SKPayment paymentWithProduct:product];
+ [[SKPaymentQueue defaultQueue] addPayment:payment];
+
+ NSLog(@"purchase sent!");
+
+ return OK;
+}
+
+- (void)reset {
+ [self.loadedProducts removeAllObjects];
+ [self.pendingRequests removeAllObjects];
+}
+
+- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
+ [self.pendingRequests removeObject:request];
+
+ Dictionary ret;
+ ret["type"] = "product_info";
+ ret["result"] = "error";
+ ret["error"] = String::utf8([error.localizedDescription UTF8String]);
+
+ InAppStore::get_singleton()->_post_event(ret);
+}
+
+- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
+ [self.pendingRequests removeObject:request];
+
+ NSArray *products = response.products;
+ [self.loadedProducts addObjectsFromArray:products];
+
+ Dictionary ret;
+ ret["type"] = "product_info";
+ ret["result"] = "ok";
+ PackedStringArray titles;
+ PackedStringArray descriptions;
+ PackedFloat32Array prices;
+ PackedStringArray ids;
+ PackedStringArray localized_prices;
+ PackedStringArray currency_codes;
+
+ for (NSUInteger i = 0; i < [products count]; i++) {
+ SKProduct *product = [products objectAtIndex:i];
+
+ const char *str = [product.localizedTitle UTF8String];
+ titles.push_back(String::utf8(str != NULL ? str : ""));
+
+ str = [product.localizedDescription UTF8String];
+ descriptions.push_back(String::utf8(str != NULL ? str : ""));
+ prices.push_back([product.price doubleValue]);
+ ids.push_back(String::utf8([product.productIdentifier UTF8String]));
+ localized_prices.push_back(String::utf8([product.localizedPrice UTF8String]));
+ currency_codes.push_back(String::utf8([[[product priceLocale] objectForKey:NSLocaleCurrencyCode] UTF8String]));
+ }
+
+ ret["titles"] = titles;
+ ret["descriptions"] = descriptions;
+ ret["prices"] = prices;
+ ret["ids"] = ids;
+ ret["localized_prices"] = localized_prices;
+ ret["currency_codes"] = currency_codes;
+
+ PackedStringArray invalid_ids;
+
+ for (NSString *ipid in response.invalidProductIdentifiers) {
+ invalid_ids.push_back(String::utf8([ipid UTF8String]));
+ }
+
+ ret["invalid_ids"] = invalid_ids;
+
+ InAppStore::get_singleton()->_post_event(ret);
+}
+
+@end
+
+@interface GodotTransactionsObserver : NSObject <SKPaymentTransactionObserver>
+
+@property(nonatomic, assign) BOOL shouldAutoFinishTransactions;
+@property(nonatomic, strong) NSMutableDictionary *pendingTransactions;
+
+- (void)finishTransactionWithProductID:(NSString *)productID;
+- (void)reset;
+
+@end
+
+@implementation GodotTransactionsObserver
+
+- (instancetype)init {
+ self = [super init];
+
+ if (self) {
+ [self godot_commonInit];
+ }
+
+ return self;
+}
+
+- (void)godot_commonInit {
+ self.pendingTransactions = [NSMutableDictionary new];
+}
+
+- (void)finishTransactionWithProductID:(NSString *)productID {
+ SKPaymentTransaction *transaction = self.pendingTransactions[productID];
+
+ if (transaction) {
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ }
+
+ self.pendingTransactions[productID] = nil;
+}
+
+- (void)reset {
+ [self.pendingTransactions removeAllObjects];
+}
+
+- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions {
+ printf("transactions updated!\n");
+ for (SKPaymentTransaction *transaction in transactions) {
+ switch (transaction.transactionState) {
+ case SKPaymentTransactionStatePurchased: {
+ printf("status purchased!\n");
+ String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
+ String transactionId = String::utf8([transaction.transactionIdentifier UTF8String]);
+ InAppStore::get_singleton()->_record_purchase(pid);
+ Dictionary ret;
+ ret["type"] = "purchase";
+ ret["result"] = "ok";
+ ret["product_id"] = pid;
+ ret["transaction_id"] = transactionId;
+
+ NSData *receipt = nil;
+ int sdk_version = [[[UIDevice currentDevice] systemVersion] intValue];
+
+ NSBundle *bundle = [NSBundle mainBundle];
+ // Get the transaction receipt file path location in the app bundle.
+ NSURL *receiptFileURL = [bundle appStoreReceiptURL];
+
+ // Read in the contents of the transaction file.
+ receipt = [NSData dataWithContentsOfURL:receiptFileURL];
+
+ NSString *receipt_to_send = nil;
+
+ if (receipt != nil) {
+ receipt_to_send = [receipt base64EncodedStringWithOptions:0];
+ }
+ Dictionary receipt_ret;
+ receipt_ret["receipt"] = String::utf8(receipt_to_send != nil ? [receipt_to_send UTF8String] : "");
+ receipt_ret["sdk"] = sdk_version;
+ ret["receipt"] = receipt_ret;
+
+ InAppStore::get_singleton()->_post_event(ret);
+
+ if (self.shouldAutoFinishTransactions) {
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ } else {
+ self.pendingTransactions[transaction.payment.productIdentifier] = transaction;
+ }
+
+ } break;
+ case SKPaymentTransactionStateFailed: {
+ printf("status transaction failed!\n");
+ String pid = String::utf8([transaction.payment.productIdentifier UTF8String]);
+ Dictionary ret;
+ ret["type"] = "purchase";
+ ret["result"] = "error";
+ ret["product_id"] = pid;
+ ret["error"] = String::utf8([transaction.error.localizedDescription UTF8String]);
+ InAppStore::get_singleton()->_post_event(ret);
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ } break;
+ case SKPaymentTransactionStateRestored: {
+ printf("status transaction restored!\n");
+ String pid = String::utf8([transaction.originalTransaction.payment.productIdentifier UTF8String]);
+ InAppStore::get_singleton()->_record_purchase(pid);
+ Dictionary ret;
+ ret["type"] = "restore";
+ ret["result"] = "ok";
+ ret["product_id"] = pid;
+ InAppStore::get_singleton()->_post_event(ret);
+ [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
+ } break;
+ default: {
+ printf("status default %i!\n", (int)transaction.transactionState);
+ } break;
+ }
+ }
+}
+
+@end
+
+void InAppStore::_bind_methods() {
+ ClassDB::bind_method(D_METHOD("request_product_info"), &InAppStore::request_product_info);
+ ClassDB::bind_method(D_METHOD("restore_purchases"), &InAppStore::restore_purchases);
+ ClassDB::bind_method(D_METHOD("purchase"), &InAppStore::purchase);
+
+ ClassDB::bind_method(D_METHOD("get_pending_event_count"), &InAppStore::get_pending_event_count);
+ ClassDB::bind_method(D_METHOD("pop_pending_event"), &InAppStore::pop_pending_event);
+ ClassDB::bind_method(D_METHOD("finish_transaction"), &InAppStore::finish_transaction);
+ ClassDB::bind_method(D_METHOD("set_auto_finish_transaction"), &InAppStore::set_auto_finish_transaction);
+}
+
+Error InAppStore::request_product_info(Dictionary p_params) {
+ ERR_FAIL_COND_V(!p_params.has("product_ids"), ERR_INVALID_PARAMETER);
+
+ PackedStringArray pids = p_params["product_ids"];
+ printf("************ request product info! %i\n", pids.size());
+
+ NSMutableArray *array = [[NSMutableArray alloc] initWithCapacity:pids.size()];
+ for (int i = 0; i < pids.size(); i++) {
+ printf("******** adding %s to product list\n", pids[i].utf8().get_data());
+ NSString *pid = [[NSString alloc] initWithUTF8String:pids[i].utf8().get_data()];
+ [array addObject:pid];
+ };
+
+ NSSet *products = [[NSSet alloc] initWithArray:array];
+
+ [products_request_delegate performRequestWithProductIDs:products];
+
+ return OK;
+}
+
+Error InAppStore::restore_purchases() {
+ printf("restoring purchases!\n");
+ [[SKPaymentQueue defaultQueue] restoreCompletedTransactions];
+
+ return OK;
+}
+
+Error InAppStore::purchase(Dictionary p_params) {
+ ERR_FAIL_COND_V(![SKPaymentQueue canMakePayments], ERR_UNAVAILABLE);
+ if (![SKPaymentQueue canMakePayments]) {
+ return ERR_UNAVAILABLE;
+ }
+
+ printf("purchasing!\n");
+ Dictionary params = p_params;
+ ERR_FAIL_COND_V(!params.has("product_id"), ERR_INVALID_PARAMETER);
+
+ NSString *pid = [[NSString alloc] initWithUTF8String:String(params["product_id"]).utf8().get_data()];
+
+ return [products_request_delegate purchaseProductWithProductID:pid];
+}
+
+int InAppStore::get_pending_event_count() {
+ return pending_events.size();
+}
+
+Variant InAppStore::pop_pending_event() {
+ Variant front = pending_events.front()->get();
+ pending_events.pop_front();
+
+ return front;
+}
+
+void InAppStore::_post_event(Variant p_event) {
+ pending_events.push_back(p_event);
+}
+
+void InAppStore::_record_purchase(String product_id) {
+ String skey = "purchased/" + product_id;
+ NSString *key = [[NSString alloc] initWithUTF8String:skey.utf8().get_data()];
+ [[NSUserDefaults standardUserDefaults] setBool:YES forKey:key];
+ [[NSUserDefaults standardUserDefaults] synchronize];
+}
+
+InAppStore *InAppStore::get_singleton() {
+ return instance;
+}
+
+InAppStore::InAppStore() {
+ ERR_FAIL_COND(instance != NULL);
+ instance = this;
+
+ products_request_delegate = [[GodotProductsDelegate alloc] init];
+ transactions_observer = [[GodotTransactionsObserver alloc] init];
+
+ [[SKPaymentQueue defaultQueue] addTransactionObserver:transactions_observer];
+}
+
+void InAppStore::finish_transaction(String product_id) {
+ NSString *prod_id = [NSString stringWithCString:product_id.utf8().get_data() encoding:NSUTF8StringEncoding];
+
+ [transactions_observer finishTransactionWithProductID:prod_id];
+}
+
+void InAppStore::set_auto_finish_transaction(bool b) {
+ transactions_observer.shouldAutoFinishTransactions = b;
+}
+
+InAppStore::~InAppStore() {
+ [products_request_delegate reset];
+ [transactions_observer reset];
+
+ products_request_delegate = nil;
+ [[SKPaymentQueue defaultQueue] removeTransactionObserver:transactions_observer];
+ transactions_observer = nil;
+}
diff --git a/modules/inappstore/in_app_store_module.cpp b/modules/inappstore/in_app_store_module.cpp
new file mode 100644
index 0000000000..039bdd4f83
--- /dev/null
+++ b/modules/inappstore/in_app_store_module.cpp
@@ -0,0 +1,48 @@
+/*************************************************************************/
+/* in_app_store_module.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "in_app_store_module.h"
+
+#include "core/config/engine.h"
+
+#include "in_app_store.h"
+
+InAppStore *store_kit;
+
+void register_inappstore_types() {
+ store_kit = memnew(InAppStore);
+ Engine::get_singleton()->add_singleton(Engine::Singleton("InAppStore", store_kit));
+}
+
+void unregister_inappstore_types() {
+ if (store_kit) {
+ memdelete(store_kit);
+ }
+}
diff --git a/modules/inappstore/in_app_store_module.h b/modules/inappstore/in_app_store_module.h
new file mode 100644
index 0000000000..44673e58bc
--- /dev/null
+++ b/modules/inappstore/in_app_store_module.h
@@ -0,0 +1,32 @@
+/*************************************************************************/
+/* in_app_store_module.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+void register_inappstore_types();
+void unregister_inappstore_types();
diff --git a/modules/inappstore/inappstore.gdip b/modules/inappstore/inappstore.gdip
new file mode 100644
index 0000000000..7a5efb8ad3
--- /dev/null
+++ b/modules/inappstore/inappstore.gdip
@@ -0,0 +1,17 @@
+[config]
+name="InAppStore"
+binary="inappstore_lib.a"
+
+initialization="register_inappstore_types"
+deinitialization="unregister_inappstore_types"
+
+[dependencies]
+linked=[]
+embedded=[]
+system=["StoreKit.framework"]
+
+capabilities=[]
+
+files=[]
+
+[plist]