torsdag den 5. december 2013

Getting in-app purchases from the App receipt

With the App receipt available it is straight forward to figure out which in-app purchases that have been bought by the user. This blogpost shows how to get that information as part of the receipt validation. Also in this installment I refer to the Receipt Validation Programming Guide.

Checking the receipt after an in-app purchase

A new receipt is automatically downloaded once a SKPaymentTransaction is changing state to SKPaymentTransactionStatePurchased. Thus, it is only a matter of validating and parsing the app-receipt again once that happens. This is checked in the delegate function
-(void)paymentQueue:(SKPaymentQueue*)queue updatedTransactions:(NSArray*)transactions

Getting the in-app part of the receipt

Below is an extension of listing 1-6 in the guide. The text in bold is the extra statements needed to hold the in-app purchase data size_t i; OCTET_STRING_t *iap[2]; int iap_cnt=0; for (i = 0; i < payload->list.count; i++) { ReceiptAttribute_t *entry; entry = payload->list.array[i]; switch (entry->type) { case 2: bundle_id = &entry->value; break; case 3: bundle_version = &entry->value; break; case 4: opaque = &entry->value; break; case 5: hash = &entry->value; break; case 17: iap[iap_cnt]=&entry->value; iap_cnt ++ ; break; } } The iap[] array should be large enough to hold references to all possible in-app purchases, i.e. the total number of products configured for the app. Each of the OCTET_STRING_t references now hold a new ASN.1 structure that holds the information on the in-app purchase. It is important that the free_struct call is not called before these new structures have been parsed. To parse the structures, a function like the following can be used: static inline void getIAP(OCTET_STRING_t **iap, int iap_cnt){ Payload_t *payload=NULL; asn_dec_rval_t rval; int i,n; for (n=0; n < iap_cnt; n++){ OCTET_STRING_t *this_iap = iap[n]; payload = NULL; rval = asn_DEF_Payload.ber_decoder(NULL,&asn_DEF_Payload,(void **)&payload, this_iap->buf,this_iap->size,0); if (rval.code != RC_OK){ NSLog(@"Failed to decode payload at index %d",n); asn_DEF_Payload.free_struct(&asn_DEF_Payload,payload,0); continue; } for (i=0; i < payload->list.count; i++){ ReceiptAttribute_t *entry; entry = payload->list.array[i]; switch (entry->type) { case AR_IAP_PRODUCT_ID:{ const char *id1 = "com.yourdomain.yourproduct1"; const char *id2 = "com.yourdomain.yourproduct2"; if (strcmp(id1, (char *)entry->value.buf+2)==0){ enableFeature(0); } if (strcmp(id2, (char *)entry->value.buf+2)==0){ enableFeature(1); } } break; default: break; } } asn_DEF_Payload.free_struct(&asn_DEF_Payload,payload,0); } } It is important that the payload is set to NULL for each iteration - otherwise it is not possible to free the payload in each iteration. Note that the OCTET_STRINGs should be parsed from offset 0, while the product IDs are found from offset 2. After this function is called, the full app receipt structure can be freed.

Restoring app purchases

Restoring app purchases using the receipt is a matter of re-fetching and re-parsing the receipt. Fetching an updated receipt can be done as shown here.

Ingen kommentarer:

Send en kommentar