Stripe-Integration iOS eCommerce Parse mit JavaScript Cloud CodeIOS

Programmierung für iOS
Anonymous
 Stripe-Integration iOS eCommerce Parse mit JavaScript Cloud Code

Post by Anonymous »

Ich baue eine E-Commerce-Plattform in meine App ein, um Zahlungen mit Kreditkarten und Apple Pay zu akzeptieren. In diesem Prozess sind zwei ViewController enthalten: BagTableViewController und AddCreditCardViewController. Es gibt auch JavaScript für den Parse Cloud Code. Ich habe den gesamten Code eingefügt.
Meine Pods sind alle auf dem neuesten Stand und haben das Parse JavaScript SDK auf Version 1.5.0 zurückgesetzt, da Parse ihre Bibliotheken nicht aktualisiert hat.
Mein aktuelles Problem tritt auf, wenn ich versuche, eine Kreditkarte im AddCreditCardViewController zu autorisieren. Nach Eingabe der von Stripe bereitgestellten Test-Kreditkarteninformationen klickt der Benutzer auf die Schaltfläche „Autorisieren“.
Wenn ich auf „Autorisieren“ klicke, erstellt Stripe die Token und Kunden, berechnet dem Kunden jedoch keine Gebühren. Stattdessen erhalte ich diesen Fehler in Xcode:

[Bolts] Warning: BFTask hat eine Ausnahme im Fortsetzungsblock abgefangen. Von diesem Verhalten wird abgeraten und es wird in einer zukünftigen Version entfernt. Abgefangene Ausnahme: *** -[__NSPlaceholderDictionary initWithObjects:forKeys:count:]: Versuch, kein Objekt aus Objekten einzufügen[1]

Bei meinen Versuchen, dieses Problem zu debuggen, habe ich diese Zeile als den Punkt gefunden, an dem ein Haltepunkt anstelle des Fehlers initiiert wird.

Code: Select all

NSDictionary *params = @{@"chargeCustomer":customerId, @"orderId":weakSelf.order.objectId};
Das macht Sinn, weil die Gebühren nicht auf Stripe erstellt werden, weil der Fehler in dieser Zeile auftritt, und die nächste Zeile ist der PFCloud-AufrufFuctionInBackround, um den Kunden zu belasten.
Ich kann nur sehr wenige Informationen zu diesem Fehler finden, aber ich glaube, ich übergebe die falschen Informationen an das NSDictionary für *params.
Kann mir jemand helfen? hier?

Code: Select all

//  BagTableViewController.h
//  Created by Chris Stahl on 6/28/16.
//  Copyright © 2016 Memory Jar. All rights reserved.

#import 
@interface BagTableViewController : UITableViewController
@end

//  BagTableViewController.m
//  Created by Chris Stahl on 6/28/16.
//  Copyright © 2016 Memory Jar.  All rights reserved.

#import "BagTableViewController.h"
#import "OrderItemTableViewCell.h"
#import "Constants.h"
#import "User.h"
#import "Order.h"
#import "OrderItem.h"
#import "Product.h"
#import "UserProfileTableViewController.h"
#import "UserPaymentMethodTableViewController.h"
#import "PaymentMethod.h"
#import "AddCreditCardViewController.h"
#import "SVProgressHUD/SVProgressHUD.h"
#import "PassKit/PassKit.h"

@interface BagTableViewController () 
@property (nonatomic, weak) IBOutlet UILabel *orderNoLabel;
@property (nonatomic, weak) IBOutlet UILabel *orderDateLabel;
@property (nonatomic, weak) IBOutlet UILabel *totalLabel;
@property (nonatomic, weak) IBOutlet UILabel *totalTextLabel;

@property (nonatomic, weak) IBOutlet UIButton *payWithCCButton;
@property (nonatomic, weak) IBOutlet UIButton *payWithApplePayButton;
@property (nonatomic, strong) Order *order;
@property (nonatomic, weak) NSArray *creditCards;
@property (nonatomic) NSDecimalNumber *amount;
@property (nonatomic, strong) PKPaymentRequest *paymentRequest;
@end

@implementation BagTableViewController

-(void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
if ([User currentUser]) {
[self queryForUnfinishedOrder];
}
}

- (void)viewDidLoad {
[self.refreshControl addTarget:self action:@selector(queryForUnfinishedOrder) forControlEvents:UIControlEventValueChanged];
}

-(void)viewWillDisappear:(BOOL)animated {
if (self.order && self.order.isDirty) {
[self.order saveInBackground];
}
}

-(IBAction)queryForUnfinishedOrder {
self.order = nil;  //to get ride of the cache
PFQuery *orderQuery = [Order queryForCustomer:[User currentUser] orderStatus:ORDER_NOT_MADE];
__weak typeof(self) weakSelf = self;
[orderQuery getFirstObjectInBackgroundWithBlock:^(PFObject *order, NSError *error){
if ([weakSelf.refreshControl isRefreshing]) {
[weakSelf.refreshControl endRefreshing];
}
if (!error) {
if (order) {
weakSelf.order = (Order *)order;
weakSelf.orderNoLabel.text = @"";
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
[dateFormatter setTimeStyle:NSDateFormatterShortStyle];
weakSelf.orderDateLabel.text = [dateFormatter stringFromDate:[NSDate date]];
weakSelf.totalLabel.text = [self.order friendlyTotal];
[weakSelf updateUI];
} else {
[weakSelf updateUI];
}

} else {
[weakSelf updateUI];
}
}];
}

-(void)updateUI {
BOOL shouldClear = self.order == nil;
if (shouldClear) {
self.orderNoLabel.text = NSLocalizedString(@"Your bag is empty.", @"");
self.orderDateLabel.text = @"";
self.totalLabel.text = @"";
self.totalTextLabel.text = @"";
self.payWithApplePayButton.hidden = YES;
self.payWithCCButton.hidden = YES;
self.payWithApplePayButton.enabled = NO;
self.payWithCCButton.enabled = NO;
} else {
self.totalTextLabel.text = NSLocalizedString(@"Total: ", @"");
self.payWithApplePayButton.hidden = NO;
self.payWithCCButton.hidden = NO;
self.payWithApplePayButton.enabled = YES;
self.payWithCCButton.enabled = YES;
}
[self.tableView reloadData];
}

#pragma Mark --- APPLE PAY PROCESS
-(IBAction)onApplePay:(id)sender{
NSString *merchantId = kAppleMerchatID;
self.paymentRequest = [Stripe paymentRequestWithMerchantIdentifier:merchantId];
if ([Stripe canSubmitPaymentRequest:self.paymentRequest]) {
[self.paymentRequest setRequiredShippingAddressFields:PKAddressFieldPostalAddress];
[self.paymentRequest setRequiredBillingAddressFields:PKAddressFieldPostalAddress];
self.paymentRequest.paymentSummaryItems = [self summaryItemsForShippingMethod:nil];
PKPaymentAuthorizationViewController *auth = [[PKPaymentAuthorizationViewController alloc] initWithPaymentRequest:self.paymentRequest];
auth.delegate = self;
if (auth) {
[self presentViewController:auth animated:YES completion:nil];
} else
[SVProgressHUD showErrorWithStatus:NSLocalizedString(@"Something Wrong", @"Something Wrong")];
} else {
[SVProgressHUD showErrorWithStatus:NSLocalizedString(@"Apple Pay is not enabled.  Please enable your Apple Pay or Pay with Credit Card.", @"")];
}
}
-(void)paymentAuthorizationViewController:(nonnull PKPaymentAuthorizationViewController *) controller didAuthorizePayment:(nonnull PKPayment *)payment completion:(nonnull void (^)(PKPaymentAuthorizationStatus))completion{
[self handlePaymentAuthorizationWithPayment:payment completion:nil];
}
-(void)paymentAuthorizationViewControllerDidFinish:(nonnull PKPaymentAuthorizationViewController *)controller {
[self dismissViewControllerAnimated:YES completion:nil];
[self queryForUnfinishedOrder];
}
- (void)handlePaymentAuthorizationWithPayment:(PKPayment *)payment completion:(void (^)(PKPaymentAuthorizationStatus))completion {
[[STPAPIClient sharedClient] createTokenWithPayment:payment
completion:^(STPToken *token, NSError *error) {
if (error) {
completion(PKPaymentAuthorizationStatusFailure);
return;
}
[self createBackendChargeWithToken:token completion:completion];
}];
}
- (void)createBackendChargeWithToken:(STPToken *)token completion:(void (^)(PKPaymentAuthorizationStatus))completion {
[self chargeWithToken:token.tokenId];
}
-(void)chargeWithToken:(NSString *)tokenId {
[self.order saveInBackgroundWithBlock:^(BOOL success, NSError *error){
if (!error) {
__weak typeof(self) weakSelf = self;
NSDictionary *params = @{@"chargeToken":tokenId, @"orderId":weakSelf.order.objectId};
[PFCloud callFunctionInBackground:@"chargeToken" withParameters:params block:^(NSString *message, NSError *error){
if (!error) {
[weakSelf queryForUnfinishedOrder];

}
}];
}
}];

}

#pragma mark - Credit Card Process
-(IBAction)onPayWithCreditCard:(id)sender{
if ([[User currentUser] isShippingAddressCompleted]) {
[self inputCreditCard];
} else {
UserProfileTableViewController *viewController = [self.storyboard instantiateViewControllerWithIdentifier:@"UserProfileTableViewController"];
[self.navigationController pushViewController:viewController animated:YES];
}
}

- (void)inputCreditCard {
AddCreditCardViewController *addCreditCardViewController = (AddCreditCardViewController *)[self.storyboard instantiateViewControllerWithIdentifier:@"AddCreditCardViewController"];
__weak typeof(self) weakSelf = self;
addCreditCardViewController.finishBlock = ^(NSString *customerId){
[weakSelf charge:customerId];
};
[self.navigationController pushViewController:addCreditCardViewController animated:YES];
}

-(void)charge:(NSString *)customerId {
[self.order saveInBackgroundWithBlock:^(BOOL success, NSError *error){
if (!error) {
__weak typeof(self) weakSelf = self;
NSDictionary *params = @{@"chargeCustomer":customerId, @"orderId":weakSelf.order.objectId};
[PFCloud callFunctionInBackground:@"chargeCustomer"  withParameters:params block:^(NSString *message, NSError *error){
if (!error) {
[weakSelf queryForUnfinishedOrder];
}
}];
}
}];
}

- (NSArray *)summaryItemsForShippingMethod:(PKShippingMethod *)shippingMethod {
NSMutableArray *purchasedItems = [NSMutableArray arrayWithCapacity:[self.order.items count]];
for (OrderItem *item in self.order.items) {
double total = item.quantity * item.product.unitPrice;
NSDecimalNumber *price = [NSDecimalNumber decimalNumberWithMantissa:total exponent:-2 isNegative:NO];
PKPaymentSummaryItem *purchasedItem = [PKPaymentSummaryItem summaryItemWithLabel:item.product.name amount:price];
[purchasedItems addObject:purchasedItem];
}
return [NSArray arrayWithArray:purchasedItems];
}

-(IBAction)onStepper:(id)sender {
UIStepper *stepper = (UIStepper *)sender;
NSInteger index = stepper.tag - 100;
NSMutableArray *orderItems = [NSMutableArray arrayWithArray:self.order.items];
OrderItem *orderItem = orderItems[index];
orderItem.quantity = (int)stepper.value;
if ((int)stepper.value == 0) {
[orderItems removeObjectAtIndex:index];
} else {
[orderItems replaceObjectAtIndex:index withObject:orderItem];
}
if ([orderItems count] == 0) {
[self showDeleteAlert];
} else {
self.order.items = [orderItems copy];
[self.tableView reloadData];
self.totalLabel.text = [self.order friendlyTotal];
}
}

#pragma mark - Table view data source
-(CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
return 80.0;
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.order.items count];
}

- (OrderItemTableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
OrderItemTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"BagItemCell" forIndexPath:indexPath];
if (self.order) [cell configureItem:self.order.items[indexPath.row] tag:indexPath.row];
else [cell configureItem:nil tag:100+indexPath.row];
return cell;
}

-(void)showDeleteAlert {
UIAlertController* alert = [UIAlertController alertControllerWithTitle:NSLocalizedString
(@"Empty Bag",@"")
message:NSLocalizedString(@"Are you sure you want to empty your bag?",@"")
preferredStyle:UIAlertControllerStyleAlert];
__weak typeof(self) weakSelf = self;
UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:NSLocalizedString
(@"Yes",@"") style:UIAlertActionStyleDefault
handler:^(UIAlertAction * action) {
[weakSelf.order deleteInBackgroundWithBlock:^(BOOL success, NSError *error){
if (!error) {
[weakSelf queryForUnfinishedOrder];
} }];
}];
UIAlertAction* cancelAction = [UIAlertAction actionWithTitle:NSLocalizedString
(@"cancel",@"") style:UIAlertActionStyleCancel
handler:^(UIAlertAction * action) {}];
[alert addAction:defaultAction];
[alert addAction:cancelAction];
[self presentViewController:alert animated:YES completion:nil];
}
@end

//  AddCreditCardViewController.h
//  Created by Chris Stahl on 6/28/16.
//  Copyright © 2016 Memory Jar.  All rights reserved.

#import 
@class AddCreditCardViewController;
typedef void (^AddCreditCardViewControllerDidFinish)(NSString *customerId);
@interface AddCreditCardViewController : UIViewController
@property (nonatomic, copy) AddCreditCardViewControllerDidFinish finishBlock;
@end

//  AddCreditCardViewController.m
//  Created by Chris Stahl on 6/28/16.
//  Copyright © 2016 Memory Jar.  All rights reserved.

#import "AddCreditCardViewController.h"
#import "Stripe/Stripe.h"
#import "User.h"
#import "PaymentMethod.h"

@interface AddCreditCardViewController ()
@property (nonatomic, weak) IBOutlet STPPaymentCardTextField *paymentView;
@property (weak, nonatomic) UIActivityIndicatorView *activityIndicator;
@end

@implementation AddCreditCardViewController

- (void)viewDidLoad {
[super viewDidLoad];

self.navigationItem.leftBarButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:UIBarButtonSystemItemCancel target:self action:@selector(onCancel:)];
self.navigationItem.rightBarButtonItem = [[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Authorize", @"") style:UIBarButtonItemStylePlain target:self action:@selector(onAuthorize:)];

UIActivityIndicatorView *activityIndicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleGray];
activityIndicator.hidesWhenStopped = YES;
self.activityIndicator = activityIndicator;
[self.view addSubview:activityIndicator];
}
- (void)paymentView:(STPPaymentCardTextField *)paymentView withCard:(STPPaymentCardTextField *)card isValid:(BOOL)valid {
self.navigationItem.rightBarButtonItem.enabled = valid;
}
- (void)paymentCardTextFieldDidChange:(STPPaymentCardTextField *)textField {
self.navigationItem.rightBarButtonItem.enabled = textField.isValid;
}
- (void)onCancel:(id)sender {
[self.navigationController popViewControllerAnimated:YES];
}

#pragma mark - Authorize the payment (get paid)

- (void)onAuthorize:(id)sender {
if (![self.paymentView isValid]) {
return;
}

STPCardParams *card = [[STPCardParams alloc] init];
card.number = self.paymentView.cardParams.number;
card.expMonth = self.paymentView.cardParams.expMonth;
card.expYear = self.paymentView.cardParams.expYear;
card.cvc = self.paymentView.cardParams.cvc;

__weak typeof(self) weakSelf = self;

[[STPAPIClient sharedClient] createTokenWithCard:card
completion:^(STPToken *token, NSError *error) {
if (error) {
} else {
User *user = [User currentUser];
NSDictionary *stripeCustomerDictionary = @{@"tokenId":token.tokenId, @"customerEmail":user.email};

[PFCloud callFunctionInBackground:@"createStripeCustomer"  withParameters:stripeCustomerDictionary block:^(NSString *customerId, NSError *error) {

if (!error) {
PaymentMethod *creditCard = [PaymentMethod object];
creditCard.owner  = user;
creditCard.stripeCustomerId = customerId;
creditCard.expirationMonth = card.expMonth;
creditCard.expirationYear = card.expYear;
creditCard.type = [creditCard friendlyType:(STPCardBrand)creditCard];
creditCard.lastFourDigit = card.last4;
creditCard.stripeCustomerId = customerId;

[creditCard saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
[weakSelf readyToCharge:customerId];
}
}];

} else {

}
}];
}
}];
}

-(void)readyToCharge:(NSString *)customerId {
self.finishBlock(customerId);
[self.navigationController popViewControllerAnimated:YES];
}
@end

Code: Select all

Parse.Cloud.define("sendNotification", function(request, response) {
var query = new Parse.Query(Parse.Installation);
var userObj = new Parse.User({
id: request.params.userId
});
query.equalTo("user", userObj);
Parse.Push.send({
where: query,
data: {
alert: request.params.message
}
}, {
success: function() {
response.success(0);
},
error: function(error) {
response.error('push notification error');
}
});
});

var Stripe = require('stripe');
Stripe.initialize('sk_test_xxx');

var STRIPE_API_BASE_URL = 'api.stripe.com/v1';
Stripe.initialize('sk_test_xxx');

var Mailgun = require('mailgun');
Mailgun.initialize("Memory_Jar", "pubkey-xxx");

//Create a stripe customer
Parse.Cloud.define("createStripeCustomer", function(request, response) {
Parse.Cloud.useMasterKey();
Parse.Promise.as().then(function() {
return Stripe.Customers.create({
description: 'customer for Memory Jar',
card: request.params.tokenId,
email: request.params.customerEmail,

}).then(null, function(error) {
console.log('Creating customer with stripe failed. Error: ' + error);
return Parse.Promise.error('An error has occurred.');
});

}).then(function(customer) {
response.success(customer.id);

}, function(error) {
response.error('error with customer creation');
});
});

//Charge the customer
Parse.Cloud.define("chargeCustomer", function(request, response) {
Parse.Cloud.useMasterKey();
var order;
var orderNo;
var total;

Parse.Promise.as().then(function() {
var orderQuery = new Parse.Query('Order');
orderQuery.equalTo('objectId', request.params.orderId);
orderQuery.include("customer");
orderQuery.include(["items.product"]);
orderQuery.descending("createdAt");

return orderQuery.first().then(null, function(error) {
return Parse.Promise.error('Sorry, this order doesn\'t exist.');
});

}).then(function(result) {
order = result;

var items = order.get("items");
for (var i = 0; i < items.length; i++) {
var item = items[i];
var unitPrice = item.get("product").get("unitPrice");
var quantity = item.get("quantity");
total += unitPrice * quantity;
}

}).then(function(result) {
var countQuery = new Parse.Query("Order");
return countQuery.count().then(null, function(error) {
return Parse.Promise.error('Something wrong.');
});

}).then(function(result) {
orderNo = result;

}).then(function(order) {
return Stripe.Charges.create({
amount: 10000, //total.toFixed(2)*100, // express dollars in cents
currency: 'usd',
customer: request.params.customerId

}).then(null, function(error) {
console.log('Charging with stripe failed. Error: ' + error);
return Parse.Promise.error('An error has occurred.  Your credit card was not charged.');
});

}).then(function(purchase) {
orderNo = 1000000 + orderNo + 1;

order.set('stripePaymentId', purchase.id);
order.set('orderStatus', 1);
order.set('orderNo', orderNo);
return order.save().then(null, function(error) {
return Parse.Promise.error('A critical error has occurred with your order. Please ' + 'contact us at your earliest convinience. ');
});

}).then(function(order) {
var greeting = "Dear ";
//                        if (request.params.firstName !== "N/A") greeting += request.params.firstName + ",\n\n";
//                        var orderId = "Order No. " + orderNo + "\n";
var body = greeting + orderId + "We have received your order for the following item(s): \n\n" + request.params.itemDesc + "\n";
var note = "Note: " + request.params.note + "\n\n";
body += "\Total: $" + 1000 + "\n\n"; //total.toFixed(2)
var thankyou = "Contact us if you have any question!\n\n" + "\n Thank you,\n";
body += thankyou;

return Mailgun.sendEmail({
to: request.params.email,
bcc: 'CUSTOMER-EMAIL',
from: 'YOUR-EMAIL',
subject: '',
text: body

}).then(null, function(error) {
return Parse.Promise.error('Your purchase was successful, but we were not able to ' + 'send you an email. Contact us at [email protected] ' + 'you have any questions.');
});

}).then(function(charge) {
response.success(charge.id);
},
function(error) {
response.error(error);
});
});

//Create Stripe token for charged customer
Parse.Cloud.define("chargeToken", function(request, response) {
Parse.Cloud.useMasterKey();
var order;
var orderNo;
var total;

Parse.Promise.as().then(function() {
var orderQuery = new Parse.Query('Order');
orderQuery.equalTo('objectId', request.params.orderId);
orderQuery.include("customer");
orderQuery.include(["items.product"]);
orderQuery.descending("createdAt");
return orderQuery.first().then(null, function(error) {
return Parse.Promise.error('Sorry, this order doesn\'t exist.');
});
}).then(function(result) {
order = result;
var items = order.get("items");
for (var i = 0; i < items.length; i++) {
var item = items[i];
var unitPrice = item.get("product").get("unitPrice");
var quantity = item.get("quantity");
total += unitPrice * quantity;
}

}).then(function(result) {
var countQuery = new Parse.Query("Order");
return countQuery.count().then(null, function(error) {
return Parse.Promise.error('Something wrong.');
});

}).then(function(result) {
orderNo = result;

}).then(function(order) {
return Stripe.Charges.create({
amount: 10000, //amount: total.toFixed(2)*100, // express dollars in cents
currency: 'usd',
card: request.params.chargeToken,

}).then(null, function(error) {
console.log('Charging with stripe failed. Error: ' + error);
return Parse.Promise.error('An error has occurred. Your credit card was not charged.');
});

}).then(function(purchase) {
orderNo = 1000000 + orderNo + 1;
order.set('orderStatus', 1); // order made
order.set('orderNo', orderNo);
order.set('stripePaymentId', purchase.id);
return order.save().then(null, function(error) {
return Parse.Promise.error('A critical error has occurred with your order. Please ' + 'contact us at your earliest convinience. ');
});

}).then(function(result) {
var greeting = "Dear ";
//                            if (order.customer.firstName !== "N/A") greeting +=
//                            order.customer.firstName + ",\n\n";
var orderId = "Order No.  " + orderNo + "\n";
var body = greeting + orderId + " We have received your order for the following item(s): \n\n" + request.params.itemDesc + "\n";
body += "\Total: $" + 1000 + "\n\n";
var thankyou = "Contact us if you have any question!\n\n" + "\n Thank you,\n";
body += thankyou;

return Mailgun.sendEmail({
to: '[email protected]', //order.customer.email,
from: 'YOUR-CONTACT-EMAIL',
subject: 'Your order was successful!',
text: body

}).then(null, function(error) {
return Parse.Promise.error('Your purchase was successful, but we were not able to ' + 'send you an email. Contact us if you have any questions.');
});

}).then(function() {
response.success('Success');
}, function(error) {
response.error(error);
});
});

Parse.Cloud.define("StripeUserCards", function(request, response) {
Parse.Cloud.httpRequest({
method: "GET",
url: "https://" + 'sk_test_bSJVNSp6BUre8e6wOzxhHYgQ' + ':@' + STRIPE_API_BASE_URL + "/customers/" + request.params.customer_id + "/cards",
success: function(cards) {
response.success(cards["data"]);
},
error: function(httpResponse) {
response.error('Request failed with response code ' + httpResponse.status);
}
});
});

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post