I have followed up the BrainTree tutorial for objective-c and ended up the following implementation. I wonder, how could I able to store user credit card information such as Uber or AirBnb. Everytime, user clicks on make a payment, and displays the credit card information entry viewcontroller.
By the way, transaction happens succesfully, and I could able to see charges on my BrainTree sandbox account.
- (IBAction)placeOrderBtnClicked:(id)sender {
[self showDropIn: TOKEN];
}
- (void)showDropIn:(NSString *)clientTokenOrTokenizationKey {
BTDropInRequest *request = [[BTDropInRequest alloc] init];
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientTokenOrTokenizationKey request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else if (result.cancelled) {
NSLog(#"CANCELLED");
[self dismissViewControllerAnimated:YES completion:NULL];
} else {
[self postNonceToServer:result.paymentMethod.nonce];
}
}];
[self presentViewController:dropIn animated:YES completion:nil];
}
- (void)postNonceToServer:(NSString *)paymentMethodNonce {
self.manager = [AFHTTPSessionManager manager];
NSDictionary *params = #{#"amount" : #"44", #"payment_method_nonce" : paymentMethodNonce};
manager.responseSerializer = [AFHTTPResponseSerializer serializer];
[manager POST:URLString parameters:params progress:nil success:^(NSURLSessionDataTask * _Nonnull operation, id _Nonnull responseObject) {
NSLog (#"transaction is succesfull");
} failure:^(NSURLSessionDataTask * _Nullable operation, NSError * _Nonnull error) {
}];
}
// the following method never gets called!!!
- (void)fetchExistingPaymentMethod:(NSString *)clientToken {
[BTDropInResult fetchDropInResultForAuthorization:clientToken handler:^(BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else {
// Use the BTDropInResult properties to update your UI
NSLog(#"Payment method :%#", result.paymentMethod);
NSLog(#"Payment Description :%#", result.paymentDescription);
NSLog(#"Payment option type :%ld", (long)result.paymentOptionType);
}
}];
}
UPDATE: I want to see the following highlighted section
Full disclosure: I work at Braintree. If you have any further questions, feel free to contact support.
Do you mean that you want the payment form to display the stored payments or are you asking how to store the payments? In order to have the Drop-in display previously stored payment methods, you need to pass the customer_id into the ClientToken.generate() call on your server-side. If you are looking to save a payment method, then again, this would happen on your server-side call, as you would have to pass the nonce from client to server and use that nonce in a PaymentMethod.create() call.
Related
Crash ScenarioI am using AFNetworking for GET and POST requests and I am calling GET and POST methods on MAIN QUEUE and when the response comes,I update the UI.Now,before the response comes from API I am pushing onto another ViewController,and that's when the crash occurs.The message says:bad_accessPossible SolutionShould I be calling that method on some background queue so that I Can update that on MAIN QUEUE.Is it correct? Here is the code:
-(void)getDataFromUrl:(NSString *)url withRequestName:(NSString *)requestName withMessege:(NSMutableDictionary *)message
{
Reachability* googleReach = [Reachability reachabilityWithHostName:#"www.google.com"];
if(googleReach.currentReachabilityStatus!=0)
{
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager GET:url parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
NSLog(#"output :%#", responseObject);
arrayParsedJson = (NSMutableArray * )responseObject;
[self.delegate dataReceivedFromService:arrayParsedJson withRequestName:requestName];
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
[self.delegate dataReceivedErrorService:error withRequestName:requestName withMsg:error.description];
}];
}
else
{
//[TSMessage showNotificationInViewController:views title:Title_Alert subtitle:Service_Alert type:TSMessageNotificationTypeError];
}
}
So when I get response in the success block,I call my delegate methods you can see.But if I have navigated to some other viewController before the response comes in block, it crashes.
I have a Mysevers Class for network written by Objective-c, and now I use mixed Objective-c and Swift project.
In my Mysevers, I have a method to network:
+(void)AFPOSTWithHud:(BOOL)hud andAddressname:(NSString*)addressName parmas:(NSDictionary*)parmas RequestSuccess:(void(^)(id result))success failBlcok:(void(^)(void))failBlcok
{
if (hud) {
[HUD addHUD];
}
AFHTTPSessionManager *requestManager = [AFHTTPSessionManager manager];
NSString *urlStr = [NSString stringWithFormat:#"%#%#",BASE_URL,addressName];
NSLog(#"%#",urlStr);
[requestManager POST:urlStr parameters:parmas progress:^(NSProgress * _Nonnull uploadProgress) {
} success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {
if (hud) {
[HUD removeHUD];
}
success(responseObject);
} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {
if (error != nil) {
NSLog(#"error==%#",[error localizedDescription]);
if (hud) {
[HUD removeHUD];
}
}
failBlcok();
}];
}
In the Swift controller, invoke the method, pass a swift Bool value false.
Mysevers.afpost(withHud: false, andAddressname: Global.url_fillMoreInfo, parmas: uploadDict, requestSuccess:{ (result) in
But, in the Mysevers class, I give a breakpoint to check the passed value, it turn out to be YES:
(lldb) po hud
YES
I am using braintree ios sdk and using their Drop in UI to add a customer card details. i am getting the UI successfully and after the customer submit the information, how can i get the paymentMEthodNounce from the result object. here is my code.
- (void)showDropIn:(NSString *)clientTokenOrTokenizationKey {
BTDropInRequest *request = [[BTDropInRequest alloc] init];
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientTokenOrTokenizationKey request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else if (result.cancelled) {
NSLog(#"CANCELLED");
} else {
// Use the BTDropInResult properties to update your UI
// result.paymentOptionType
// result.paymentMethod
// result.paymentIcon
// result.paymentDescription
}
}];
[self presentViewController:dropIn animated:YES completion:nil];}
i think when the customer submit the result, payment nounce will be result object?? if it thinking is right how can get that nounce to a variable to send it to my server. i am fairly new in ios, so any help will be appreciated.
For the new iOS v4 SDK I had to import Braintree also, I didn't have access to the paymentMethod property of the BTDropInResult otherwise.
import BraintreeDropIn
import Braintree
You implement BTDropInViewControllerDelegate to obtain the payment method nonce on success, and dismiss the Drop In UI in either case:
- (void)dropInViewController:(__unused BTDropInViewController *)viewController didSucceedWithPaymentMethod:(BTPaymentMethod *)paymentMethod {
[self postNonceToServer:paymentMethod.nonce]; // Send payment method nonce to your server
[self dismissViewControllerAnimated:YES completion:nil];
}
- (void)dropInViewControllerDidCancel:(__unused BTDropInViewController *)viewController {
[self dismissViewControllerAnimated:YES completion:nil];
}
Send payment method nonce to server
- (void)postNonceToServer:(NSString *)paymentMethodNonce {
// Update URL with your server
NSURL *paymentURL = [NSURL URLWithString:#"https://your-server.example.com/checkout"];
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:paymentURL];
request.HTTPBody = [[NSString stringWithFormat:#"payment_method_nonce=%#", paymentMethodNonce] dataUsingEncoding:NSUTF8StringEncoding];
request.HTTPMethod = #"POST";
[[[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
// TODO: Handle success and failure
}] resume];
}
BTDropInRequest *request = [[BTDropInRequest alloc] init];
request.amount = #"10";
BTDropInController *dropIn = [[BTDropInController alloc] initWithAuthorization:clientTokenOrTokenizationKey request:request handler:^(BTDropInController * _Nonnull controller, BTDropInResult * _Nullable result, NSError * _Nullable error) {
if (error != nil) {
NSLog(#"ERROR");
} else if (result.cancelled) {
NSLog(#"CANCELLED");
} else {
BTPaymentMethodNonce *selectedNonce = result.paymentMethod;
[self postNonceToServer:self.selectedNonce.nonce];
}
}];
[self presentViewController:dropIn animated:YES completion:nil];
Since I am new to IOS and AFNetworking 3,0 is new, I don't know how to retrieve data from AFHTTPSessionManager.
I have to following message and I want to return the result
- (NSString *) makeServiceCall;
{
NSString *response = #"";
#try {
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager
POST:self.url.absoluteString
parameters:self.parameters
progress:nil
success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Success: %#", responseObject);}
failure:^(NSURLSessionDataTask * task, NSError * error) {
NSLog(#"Error: %#", error);
}];
[AFHTTPSessionManager manager].securityPolicy.allowInvalidCertificates = YES;
}
#catch (NSException *exception) {
NSLog(#"%#", exception.reason);
}
}
The method AFHTTPSessionManager POST:parameters:progress:success:failure: is an asynchronous method.
What you are trying to do is return a string from the method calling it. This will not work as the method will finish before the download has started.
You need to call this with a completion block something like this...
- (void)getStringWithCompletionHandler:(void (^)(id))completion {
NSLog(#"Method started");
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
[manager
POST:self.url.absoluteString
parameters:self.parameters
progress:^(NSProgress * _Nonnull uploadProgress) {
NSLog(#"Download underway");
}
success:^(NSURLSessionDataTask *task, id responseObject) {
NSLog(#"Download successful");
completion(responseObject);
}
failure:^(NSURLSessionDataTask * task, NSError * error) {
NSLog(#"Error");
}];
// trying to return a string here won't work because the download hasn't finished yet.
// You can see the order of things happening by adding logs...
NSLog(#"Method finished");
}
The order of the logs in this code will be...
Method started
Method finished
Download underway
Download successful
As you can see, trying to return at the end of the method won't work because the download won't have completed yet.
I have a scenario where I need to quiet refresh auth token (relogin) again if it expired when I accessing other API but I'm having a hard time thinking how to code this without creating redundant codes for every APIs even though the flow is similar.
When user has expired auth token > call paid API A (return 401 unauthorised) > relogin again > call paid API A (run successfully)
I'm having difficult in wrapping my mind to call paid API A the second time with less code and not falling into infinite loop trap. Is there any method useful for this case like NSNotification center?
Note: I need to use API in this format from AFNetworkinglogin
- (NSURLSessionDataTask *)getApiA:(CallbackBlock)block{
CallbackBlock _block = [block copy];
NSString *urlString = [[NSURL URLWithString:GET_API_A_URL relativeToURL:[NSURL URLWithString:HOME_URL]] absoluteString];
return [self GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = (NSDictionary *)responseObject;
BLOCK_SAFE_RUN(block, response, nil, task);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if([self unauthorizedAccess:task]){ //401
***//call Login once again > run getApiA again***
}else if ([self forbiddenAccess:task]){ //403
}
BLOCK_SAFE_RUN(block, nil, error, task);
}];
}
If i get it right you could split it into 2 methods. And pass a bool for trying again. e.g.:
- (NSURLSessionDataTask *)getApiA:(id)block {
NSString *urlString = [[NSURL URLWithString:GET_API_A_URL relativeToURL:[NSURL URLWithString:HOME_URL]] absoluteString];
return [self doApiACallWithURL:urlString firstTry:YES completion:block];
}
- (NSURLSessionDataTask *)doApiACallWithURL:(NSString *)url firstTry:(BOOL)first completion:(CallbackBlock)completion {
__weak typeof(self) wself = self;
return [self GET:urlString parameters:nil success:^(NSURLSessionDataTask *task, id responseObject) {
NSDictionary *response = (NSDictionary *)responseObject;
BLOCK_SAFE_RUN(block, response, nil, task);
} failure:^(NSURLSessionDataTask *task, NSError *error) {
if ([wself unauthorizedAccess:task]) { //401
if (first) {
[wself doApiACallWithURL:url firstTry:NO completion:completion];
}
} else if ([wself forbiddenAccess:task]) { //403
}
BLOCK_SAFE_RUN(block, nil, error, task);
}];
}
and use a weak self for blocks is in most cases a good idea.