• Hello [name]! Thanks for joining the GMC. Before making any posts in the Tech Support forum, can we suggest you read the forum rules? These are simple guidelines that we ask you to follow so that you can get the best help possible for your issue.

iOS In-App Purchases stuck in iap_failed state

Hello!

I have followed the official Gamemaker tutorial on In-App purchases, and made some tweaks because it crashed the game, and have gotten it to the point in which I can tap on a purchase button. However, iap_activate does not seem to work correctly, leading the game to return an iap_failed state every time. My device is connected to the Store and should be running a Sandbox account. I also made sure to click "Create Executable".

Here is the code:

<Create Event>


/// Load IAP

// Store the product names in variables for easy editing
product_pack1 = "(omitted but should be valid)";
product_pack2 = "(omitted but should be valid)";
product_pack3 = "(omitted but should be valid)";
var map_create = true; // This is to tell us if we need to create the save map
if (file_exists("iap_data.dat")) // Check for the file
{
// The file exists so load the save map
global.savemap= ds_map_secure_load("iap_data.dat");
// Check the map has been loaded correctly
if (ds_exists(global.savemap, ds_type_map))
{
map_create = false;
// Check to see if the non-consumable IAP has been used
if (ds_map_exists(global.savemap, product_pack1))
{
if (ds_map_find_value(global.savemap, product_pack1) == false)
{
// The non-consumable IAP has not been used so do something
// like enable ads, or disable extra content, etc...
}
}
}
}

// The save map doesn't exist, so create it and initialise the IAPs to false
if (map_create)
{
global.savemap= ds_map_create();
//ds_map_add(global.savemap, product_pack1, false);
//ds_map_add(global.savemap, "consumed", 0);
ds_map_secure_save(global.savemap, "iap_data.dat");
}

var productList = ds_list_create();

// Create non-consumable iap
//ds_map_add(durable_map , "id", product_durable);
//ds_map_add(durable_map , "title", "Test Durable");
//ds_map_add(durable_map , "type", "Durable");
//ds_list_add(productList, durable_map);


// Create consumable IAP
var product_map1 = ds_map_create();
ds_map_add(product_map1, "id", product_pack1);
ds_map_add(product_map1, "title", "Continue Pack 1");
ds_map_add(product_map1, "description", "Receive 75 Continues!");
ds_map_add(product_map1, "price", "$0.99");
ds_map_add(product_map1, "type", "Consumable");

var product_map2 = ds_map_create();
ds_map_add(product_map2, "id", product_pack2);
ds_map_add(product_map2, "title", "Continue Pack 2");
ds_map_add(product_map2, "description", "Receive 300 Continues!");
ds_map_add(product_map2, "price", "$2.99");
ds_map_add(product_map2, "type", "Consumable");

var product_map3 = ds_map_create();
ds_map_add(product_map3, "id", product_pack3);
ds_map_add(product_map3, "title", "Continue Pack 3");
ds_map_add(product_map3, "description", "Receive 600 Continues!");
ds_map_add(product_map3, "price", "$4.99");
ds_map_add(product_map3, "type", "Consumable");

ds_list_add(productList, product_map1);
ds_list_add(productList, product_map2);
ds_list_add(productList, product_map3);

// Activate IAP
iap_activate(productList);

// Clean up
//ds_map_destroy(durable_map);
ds_map_destroy(product_map1);
ds_map_destroy(product_map2);
ds_map_destroy(product_map3);
ds_list_destroy(productList);





< Async IAP Event >
// Get the event type

var _id = iap_data[? "type"];
// Perform different code depending on the type
switch (_id)
{
case iap_ev_storeload: // Check to see if the store has loaded or not
if (iap_data[? "status"] == iap_storeload_ok)
{
show_debug_message("STORE LOADED");
}
else show_debug_message("STORE NOT LOADED");
break;

case iap_ev_product: // Get product details
var _product = iap_data[? "index"];
var _map = ds_map_create();
iap_product_details(_product, _map);
show_message_async("PRODCT ACTIVATED - " + string(_product));
show_message_async("ID - " + string(_map[? "id"]));
show_message_async("Title - " + string(_map[? "title"]));
show_message_async("Description - " + string(_map[? "description"]));
show_message_async("Price - " + string(_map[? "price"]));
ds_map_destroy(_map);
break;

case iap_ev_purchase:
var _product = iap_data[? "index"];
var _map = ds_map_create();
iap_purchase_details(_product, _map);
if (_map[? "verified"])
{
show_message_async("TRUE");
}
else
{
show_message_async("FALSE");
}
switch (_map[? "status"])
{
case iap_available:
show_message_async("iap_available");
break;
case iap_failed:
show_message_async("iap_failed");
break;
case iap_purchased:

show_message_async("iap_purchased");
if _map[? "product"] == product_pack1
{
show_message("Consumable IAP Purchased");
iap_consume(product_pack1);
}
if _map[? "product"] == product_pack2
{
show_message("Consumable IAP Purchased");
iap_consume(product_pack2);
}
if _map[? "product"] == product_pack3
{
show_message("Consumable IAP Purchased");
iap_consume(product_pack3);
}
break;
case iap_canceled:
show_message_async("iap_canceled");
break;
case iap_refunded:
show_message_async("iap_refunded");
break;
}
ds_map_destroy(_map);
break;

case iap_ev_consume:
if iap_data[? "product"] == product_pack1
{
global.savemap[? "consumed"] += 75;
ds_map_secure_save(global.savemap, "iap_data.dat");
}
if iap_data[? "product"] == product_pack2
{
global.savemap[? "consumed"] += 300;
ds_map_secure_save(global.savemap, "iap_data.dat");
}
if iap_data[? "product"] == product_pack3
{
global.savemap[? "consumed"] += 600;
ds_map_secure_save(global.savemap, "iap_data.dat");
}
break;

case iap_ev_restore:
if iap_data[? "result"]
{
show_message_async("Purchases restored!");
}
else
{
show_message_async("Purchases were not able to be restored.");
}
break;
}

Any help would be greatly appreciated!

Thanks,
Blakecubed

PS We're not abusing IAP functionality, don't worry :p
 
Top