I'm fairly new to programming and I created an app to charge customers and would like to store their CC information and charge it at a later time. I've been going through all the tutorials and documentation and I am unable to follow how I can integrate this into my app. Do I need to know other technical skills such as Rest API, Curl, Ruby, etc to get this set up? All the guides and documentation is pointing to that direction. I don't really understand what GET/POST is for and how that fits into iOS Objective-C programming.
Any guidance on how to set this up would be tremendously appreciated. I've been stuck on this for some time now.
Stripe is the easiest way to support Apple Pay, which enables frictionless card payments and eliminates the need to manually type card or shipping details. Your customers authorize payments with Touch ID, Face ID, or passcode.
Stripe's Software Development Kit (SDK) is available for JavaScript, Android, and iOS. This gives you the flexibility to integrate the Stripe card reader into your existing point of sale (POS) system or even build a new one.
How are you storing their CC information to charge it at a later time? Before proceeding, you need to know if it is PCI compliant or not. At most, the only things you should be looking to store is the expiration date, last 4 digits, and an associated record object that Parse Stripe gives you that corresponds to that CC. Do not try to store the full CC.
As to your other questions:
Generally you need to know a web language to do something like this. Here is an example of a possible stack that I've seen in a situation like this:
iOS App -> sends request to Server (rails, python, php, etc) -> Will send request to 3rd party site
3rd party site response -> Server -> iOS app.
The point of the server is to intercept the call from the mobile App to Parse, and the response from Parse back to the mobile app. The reason for this is so you can have a "master" db of the transactions/states and can recover if the app is ever reinstalled on the user's phone. It also will let you store an identifier that points to the user's CC on parse stripe (I'm assuming).
You should really understand GET/POST as they are becoming a very basic feature of any iOS app. They are simply how you get/insert records from a server. Considering almost all of the popular apps have some kind of network connectivity embedded in them, it really is a core part of iOS programming IMO.
Parse's stripe API is not as complete as it could and should be. There are many features it does not include natively, but can be accomplished VIA an HTTP Request. I had to learn a little bit of Javascript, and HTTP request to get many features working. Of course your first instinct should tell you do NOT store a CC number on any device ever! Anytime you have a user input a CC number, immediately get a token and then that is all you will need to use.
Luckily stripe gives you the ability to save customers, and attached CC to customers, and then charge that customer in the future without getting the CC number again. Parse's api does not handle adding a CC to a customer so I added the feature myself.
So Step 1 and 2 Generate a Customer using Parse's API, and generate a Token from the CC information they enter again using Parse's API. If you need help with this, and the cloud code required let me know.
Step 3 Add a CC to a customer. I'm using a custom Customer object, but the main thing you really need is the stripe customerId which is customer.identifier in my code, and tokenID from your CC which in my case is token.tokenId. The response back will be a JSON string with the card information, I turn this into a Dictionary, and then create a STPCard from the dictionary. Also I show how to remove a card from a customer.
iOS Code:
+(void)addToken:(STPToken *)token toCustomerId:(NSString *)customerId completionHandler:(PFIdResultBlock)block
{
[PFCloud callFunctionInBackground:@"stripeUpdateCustomer" withParameters:@{@"customerId":customerId,@"data":@{@"card":token.tokenId}} block:block];
}
+ (void)removeCard:(STPCard *)card FromCustomer:(ELCustomer *)customer completion:(STPCardDeletionBlock)handler
{
if (!customer ||!customer.identifier || !card || !card.identifier || !handler) [NSException raise:@"RequiredParameter" format:@"Required Parameter Missing for deleting card from customer"];
[PFCloud callFunctionInBackground:@"stripeDeleteCardFromCustomer" withParameters:@{@"cardId":card.identifier,@"customerId":customer.identifier} block:^(id object, NSError *error)
{
NSDictionary *dict = nil;
NSError *jsonError = nil;
if (object && [object isKindOfClass:[NSString class]] && !error) {
dict = [NSJSONSerialization JSONObjectWithData:[object dataUsingEncoding:NSUTF8StringEncoding] options:kNilOptions error:&jsonError];
}
if (!jsonError && dict) {
handler(dict[@"id"],[dict[@"deleted"] boolValue],error);
}
else if(jsonError) handler(nil,NO,jsonError);
else handler(nil,NO,error);
}];
}
Cloud Code Required:
Parse.Cloud.define("stripeUpdateCustomer", function(request, response)
{
Stripe.Customers.update
(
request.params["customerId"],
request.params["data"],
{
success:function(results)
{
console.log(results["id"]);
response.success(results);
},
error:function(error)
{
response.error("Error:" +error);
}
}
);
});
Parse.Cloud.define("stripeDeleteCardFromCustomer", function(request, response)
{
Stripe.initialize(STRIPE_SECRET_KEY);
Parse.Cloud.httpRequest({
method:"DELETE",
//STRIPE_SECRET_KEY will be your stripe secrect key obviously, this is different from the public key that you will use in your iOS/Android side.
// STRIPE_API_BASE_URL = 'api.stripe.com/v1'
url: "https://" + STRIPE_SECRET_KEY + ':@' + STRIPE_API_BASE_URL + "/customers/" + request.params.customerId + "/cards/" + request.params.cardId,
success: function(httpResponse) {
response.success(httpResponse.text);
},
error: function(httpResponse) {
response.error('Request failed with response code ' + httpResponse.status);
}
});
});
iOS Code for applying a charge to a customer or token notice the required parameters in the dictionary are an amount in cents not dollars, a currency, and then either a customer or a tokenId. Note a customer can have many credit cards, but one of them is the active credit card. The active card is the card that will be charged when you charge a customer:
//Will attempt to charge customer, if no customer exists, or it fails to charge the custoemr it will attempt to charge a card token directly;
//*********Warning: This is the final step it will APPLY A CHARGE TO THE ACCOUNT.***************
-(void)processChargeThroughStripeWithCompletionHandler:(STPChargeCompletionHandler)handler
{
if (![self validForCardProcessing] && ![self validForCustomerProcessing]) {
handler(nil,[NSError errorWithDomain:MY_ERROR_DOMAIN code:elErrorCodeNoCustomerOrTokenID userInfo:[NSDictionary dictionary]]);
return;
}
[self processChargeThroughStripeUsingCustomerWithCompletionHandler:^(STPCharge *charge, NSError *error)
{
if (!error) handler(charge,error);
else{
[self processChargeThroughStripeUsingCardWithCompletionHandler:^(STPCharge *charge, NSError *error) {
handler(charge, error);
}];
}
}];
}
//Process payment using a customer to their active card. No token is required if customer exists with a card on record.
//*********Warning: This is the final step it will APPLY A CHARGE TO THE ACCOUNT.***************
-(void)processChargeThroughStripeUsingCustomerWithCompletionHandler:(STPChargeCompletionHandler)handler
{
if (!self.validForCustomerProcessing)
{
handler(self,[NSError errorWithDomain:MY_ERROR_DOMAIN code:elErrorCodeNoCustomerID userInfo:[NSDictionary dictionary]]);
return;
}
[PFCloud callFunctionInBackground:@"chargeToken" withParameters:[STPCharge dictionaryFromSTPChargeForProccessingUsingCustomer:self] block:^(id object, NSError *error)
{
if (!error)
{
[self initSelfWithDictionary:object];
NSLog(@"object:%@",object);
}
handler(self,error);
}];
}
//Process payment using a token that is attached to the charge, when complete self will be updated with the new charge information
//*********Warning: This is the final step it will APPLY A CHARGE TO THE ACCOUNT.***************
-(void)processChargeThroughStripeUsingCardWithCompletionHandler:(STPChargeCompletionHandler)handler
{
if (!self.validForCardProcessing)
{
handler(self,[NSError errorWithDomain:MY_ERROR_DOMAIN code:elErrorCodeNoTokenID userInfo:[NSDictionary dictionary]]);
return;
}
[PFCloud callFunctionInBackground:@"chargeToken" withParameters:[STPCharge dictionaryFromSTPChargeForProccessingUsingCard:self] block:^(id object, NSError *error)
{
if (!error)
{
[self initSelfWithDictionary:object];
}
handler(self,error);
}];
}
+ (NSDictionary *)dictionaryFromSTPChargeForProccessingUsingCard:(STPCharge *)charge
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
dictionary[@"amount"] = charge.amountInCents;
dictionary[@"currency"] = charge.currency;
dictionary[@"card"] = charge.token.tokenId;
return dictionary;
}
+ (NSDictionary *)dictionaryFromSTPChargeForProccessingUsingCustomer:(STPCharge *)charge
{
NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
dictionary[@"amount"] = charge.amountInCents;
dictionary[@"currency"] = charge.currency;
dictionary[@"customer"] = charge.customer.identifier;
return dictionary;
}
Cloud code for charging a customer/token:
Parse.Cloud.define("chargeToken",function(request,response)
{
Stripe.initialize(STRIPE_SECRET_KEY);
Stripe.Charges.create
(
request.params,
{
success:function(results)
{
response.success(results);
},
error:function(error)
{
response.error("Error:" +error);
}
}
);
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With