ios6 - iOS inapp payments - what is the right way to confirm to the user when they have made a purchase? -
i confused how , when tell user completed purchase successfully. got application rejected during app review process reason:
1. launch app 2. tap on learn benefits of subscription 3. tap on subscribe 4. tap on confirm , enter itunes password 5. no further action occurs
and not sure when , how tell user entered info correctly since confirmed on itunes server.
i have iaphelper class looks this:
// // iaphelper.m // businessplan // // created macoslion on 8/12/13. // // // 1 #import "iaphelper.h" #import <storekit/storekit.h> // 2 //@interface iaphelper () <skproductsrequestdelegate> @interface iaphelper () <skproductsrequestdelegate, skpaymenttransactionobserver> @end @implementation iaphelper { // 3 skproductsrequest * _productsrequest; // 4 requestproductscompletionhandler _completionhandler; nsset * _productidentifiers; nsmutableset * _purchasedproductidentifiers; } - (id)initwithproductidentifiers:(nsset *)productidentifiers { if ((self = [super init])) { // store product identifiers _productidentifiers = productidentifiers; // check purchased products _purchasedproductidentifiers = [nsmutableset set]; (nsstring * productidentifier in _productidentifiers) { bool productpurchased = [[nsuserdefaults standarduserdefaults] boolforkey:productidentifier]; if (productpurchased) { [_purchasedproductidentifiers addobject:productidentifier]; nslog(@"previously purchased: %@", productidentifier); // set memory yes , use later. // user data. nsuserdefaults *standarduserdefaults = [nsuserdefaults standarduserdefaults]; // first time on app, set user cookie. [standarduserdefaults setbool:yes forkey:@"subscriber"]; // saving [[nsuserdefaults standarduserdefaults] synchronize]; } else { nslog(@"not purchased: %@", productidentifier); } } [[skpaymentqueue defaultqueue] addtransactionobserver:self]; } return self; } // retrieve product information itunes connect - (void)requestproductswithcompletionhandler:(requestproductscompletionhandler)completionhandler { // 1 _completionhandler = [completionhandler copy]; // 2 _productsrequest = [[skproductsrequest alloc] initwithproductidentifiers:_productidentifiers]; _productsrequest.delegate = self; [_productsrequest start]; } #pragma mark - skproductsrequestdelegate - (void)productsrequest:(skproductsrequest *)request didreceiveresponse:(skproductsresponse *)response { nslog(@"loaded list of products..."); _productsrequest = nil; nsarray * skproducts = response.products; (skproduct * skproduct in skproducts) { nslog(@"found product: %@ %@ %0.2f", skproduct.productidentifier, skproduct.localizedtitle, skproduct.price.floatvalue); } _completionhandler(yes, skproducts); _completionhandler = nil; } - (void)request:(skrequest *)request didfailwitherror:(nserror *)error { nslog(@"failed load list of products."); _productsrequest = nil; _completionhandler(no, nil); _completionhandler = nil; } - (bool)productpurchased:(nsstring *)productidentifier { return [_purchasedproductidentifiers containsobject:productidentifier]; } - (void)buyproduct:(skproduct *)product { nslog(@"buying %@...", product.productidentifier); skpayment * payment = [skpayment paymentwithproduct:product]; [[skpaymentqueue defaultqueue] addpayment:payment]; } - (void)paymentqueue:(skpaymentqueue *)queue updatedtransactions:(nsarray *)transactions { (skpaymenttransaction * transaction in transactions) { switch (transaction.transactionstate) { case skpaymenttransactionstatepurchased: [self completetransaction:transaction]; break; case skpaymenttransactionstatefailed: [self failedtransaction:transaction]; break; case skpaymenttransactionstaterestored: [self restoretransaction:transaction]; default: break; } }; } - (void)completetransaction:(skpaymenttransaction *)transaction { nslog(@"completetransaction..."); [self providecontentforproductidentifier:transaction.payment.productidentifier]; [[skpaymentqueue defaultqueue] finishtransaction:transaction]; // set memory yes , use later. // user data. nsuserdefaults *standarduserdefaults = [nsuserdefaults standarduserdefaults]; // first time on app, set user cookie. [standarduserdefaults setbool:yes forkey:@"subscriber"]; // saving [[nsuserdefaults standarduserdefaults] synchronize]; // tell user things purchased. // message person can't connect server // uialertview *message = [[uialertview alloc] initwithtitle:@"success sending purchase request." // message:@"just press ok , wait few moments while itunes processes request." delegate:nil cancelbuttontitle:@"ok" otherbuttontitles:nil]; // // [message show]; } - (void)restoretransaction:(skpaymenttransaction *)transaction { nslog(@"restoretransaction..."); [self providecontentforproductidentifier:transaction.originaltransaction.payment.productidentifier]; [[skpaymentqueue defaultqueue] finishtransaction:transaction]; } - (void)failedtransaction:(skpaymenttransaction *)transaction { nslog(@"failedtransaction..."); if (transaction.error.code != skerrorpaymentcancelled) { nslog(@"transaction error: %@", transaction.error.localizeddescription); // message person can't connect server uialertview *message = [[uialertview alloc] initwithtitle:@"could not complete transaction" message:@"please try again. if error persists, please email support at: alex@problemio.com" delegate:nil cancelbuttontitle:@"ok" otherbuttontitles:nil]; [message show]; } [[skpaymentqueue defaultqueue] finishtransaction: transaction]; } // add top of file nsstring *const iaphelperproductpurchasednotification = @"iaphelperproductpurchasednotification"; // add new method - (void)providecontentforproductidentifier:(nsstring *)productidentifier { //nslog(@"provifing content subsciber: "); // message person can't connect server uialertview *message = [[uialertview alloc] initwithtitle:@"subscribed successfully!" message:@"now can ask questions right on app, , our monthly business content." delegate:nil cancelbuttontitle:@"ok" otherbuttontitles:nil]; [message show]; [_purchasedproductidentifiers addobject:productidentifier]; [[nsuserdefaults standarduserdefaults] setbool:yes forkey:productidentifier]; [[nsuserdefaults standarduserdefaults] synchronize]; [[nsnotificationcenter defaultcenter] postnotificationname:iaphelperproductpurchasednotification object:productidentifier userinfo:nil]; } @end
and class start transaction process:
#import "subscriptioncontroller.h" // 1 #import "rageiaphelper.h" #import <storekit/storekit.h> // 2 @interface subscriptioncontroller () { nsarray *_products; // add new instance variable class extension nsnumberformatter * _priceformatter; } @end @implementation subscriptioncontroller // 3 - (void)viewdidload { [super viewdidload]; //self.refreshcontrol = [[uirefreshcontrol alloc] init]; //[self.refreshcontrol addtarget:self action:@selector(reload) forcontrolevents:uicontroleventvaluechanged]; [self reload]; //[self.refreshcontrol beginrefreshing]; // add end of viewdidload _priceformatter = [[nsnumberformatter alloc] init]; [_priceformatter setformatterbehavior:nsnumberformatterbehavior10_4]; [_priceformatter setnumberstyle:nsnumberformattercurrencystyle]; self.view.backgroundcolor = [uicolor colorwithwhite:0.859 alpha:1.000]; } // 4 - (void)reload { _products = nil; //[self.tableview reloaddata]; [[rageiaphelper sharedinstance] requestproductswithcompletionhandler:^(bool success, nsarray *products) { if (success) { _products = products; //[self.tableview reloaddata]; } //[self.refreshcontrol endrefreshing]; }]; } #pragma mark - table view - (nsinteger)numberofsectionsintableview:(uitableview *)tableview { return 1; } // 5 - (nsinteger)tableview:(uitableview *)tableview numberofrowsinsection:(nsinteger)section { return _products.count; } - (uitableviewcell *)tableview:(uitableview *)tableview cellforrowatindexpath:(nsindexpath *)indexpath { nslog(@"a"); uitableviewcell *cell = [tableview dequeuereusablecellwithidentifier:@"cell" forindexpath:indexpath]; skproduct * product = (skproduct *) _products[indexpath.row]; cell.textlabel.text = product.localizedtitle; // add bottom of tableview:cellforrowatindexpath (before return cell) [_priceformatter setlocale:product.pricelocale]; cell.detailtextlabel.text = [_priceformatter stringfromnumber:product.price]; if ([[rageiaphelper sharedinstance] productpurchased:product.productidentifier]) { cell.accessorytype = uitableviewcellaccessorycheckmark; cell.accessoryview = nil; } else { uibutton *buybutton = [uibutton buttonwithtype:uibuttontyperoundedrect]; buybutton.frame = cgrectmake(0, 0, 72, 37); [buybutton settitle:@"buy" forstate:uicontrolstatenormal]; buybutton.tag = indexpath.row; [buybutton addtarget:self action:@selector(buybuttontapped:) forcontrolevents:uicontroleventtouchupinside]; cell.accessorytype = uitableviewcellaccessorynone; cell.accessoryview = buybutton; } return cell; } //- (ibaction)subscribe:(id)sender //{ // uibutton *buybutton = (uibutton *)sender; // skproduct *product = _products[buybutton.tag]; // // nslog(@"buying %@...", product.productidentifier); // [[rageiaphelper sharedinstance] buyproduct:product]; //} - (void)viewwillappear:(bool)animated { [[nsnotificationcenter defaultcenter] addobserver:self selector:@selector(productpurchased:) name:iaphelperproductpurchasednotification object:nil]; } - (void)viewwilldisappear:(bool)animated { [[nsnotificationcenter defaultcenter] removeobserver:self]; } - (void)productpurchased:(nsnotification *)notification { nslog(@"purchaseddddddddd"); // nsstring * productidentifier = notification.object; // [_products enumerateobjectsusingblock:^(skproduct * product, nsuinteger idx, bool *stop) // { // if ([product.productidentifier isequaltostring:productidentifier]) // { // // todo: // // update how button appears. // // //// [self.table reloadrowsatindexpaths:@[[nsindexpath indexpathforrow:idx insection:0]] withrowanimation:uitableviewrowanimationfade]; // *stop = yes; // } // }]; // message person can't connect server uialertview *message = [[uialertview alloc] initwithtitle:@"purchased successfully" message:@":)" delegate:nil cancelbuttontitle:@"ok" otherbuttontitles:nil]; [message show]; // push confirmation } //- (ibaction)subscribe:(id)sender //{ // //} - (void)viewdidunload { [super viewdidunload]; } - (ibaction)createsub:(id)sender { uibutton *buybutton = (uibutton *)sender; skproduct *product = _products[buybutton.tag]; if ( product == nil) { // message person can't connect server uialertview *message = [[uialertview alloc] initwithtitle:@"pulling product data itunes..." message:@"please try again in few moments." delegate:nil cancelbuttontitle:@"ok" otherbuttontitles:nil]; [message show]; } else { // message person can't connect server uialertview *message = [[uialertview alloc] initwithtitle:@"success sending purchase request." message:@"just press ok , wait few moments while itunes processes request." delegate:nil cancelbuttontitle:@"ok" otherbuttontitles:nil]; [message show]; nslog(@"buying %@...", product.productidentifier); [[rageiaphelper sharedinstance] buyproduct:product]; } } @end
thank help!
you should have sort of ui update tell user payment successful , feature available/unlocked. typically, done either update in views correspond new content, or uialertview
if there no visual changes made.
Comments
Post a Comment