diff --git a/iOS/card.io/CardIOPGPlugin.h b/iOS/card.io/CardIOPGPlugin.h new file mode 100644 index 0000000..4f89909 --- /dev/null +++ b/iOS/card.io/CardIOPGPlugin.h @@ -0,0 +1,26 @@ +// +// CardIOPGPlugin.h +// +// Copyright 2012 Lumber Labs (card.io) +// MIT licensed +// + +#import +#import "CardIO.h" + +#ifdef CORDOVA_FRAMEWORK + #import +#else + #import "CDVPlugin.h" +#endif + +@interface CardIOPGPlugin : CDVPlugin { + CardIOPaymentViewController *paymentViewController; + NSString *scanCallbackId; +} + +- (void)scan:(NSMutableArray *)args withDict:(NSMutableDictionary *)options; +- (void)canScan:(NSMutableArray *)args withDict:(NSMutableDictionary *)options; +- (void)version:(NSMutableArray *)args withDict:(NSMutableDictionary *)options; + +@end diff --git a/iOS/card.io/CardIOPGPlugin.js b/iOS/card.io/CardIOPGPlugin.js new file mode 100644 index 0000000..0995757 --- /dev/null +++ b/iOS/card.io/CardIOPGPlugin.js @@ -0,0 +1,78 @@ +/** + * CardIOPGPlugin.js + * + * Copyright 2012 Lumber Labs (card.io) + * MIT licensed + */ + +/** + * This class exposes card.io's card scanning functionality to JavaScript. + * + * @constructor + */ +function CardIO() { +} + +/** + * Scan a credit card with card.io. + * + * @parameter appToken: a string; get it from https://www.card.io/. + * + * @parameter options: an object; may be {}. Sample options object: + * {"collect_expiry": true, "collect_cvv": false, "collect_zip": false, + * "shows_first_use_alert": true, "disable_manual_entry_buttons": false} + * Omit any key from options to get the default value. For more detail on + * each of the options, look at CardIOPaymentViewController.h. + * + * @parameter onSuccess: a callback function that accepts a response object; response keys + * include card_type, redacted_card_number, expiry_month, card_number, expiry_year, + * and, if requested, cvv and zip. + * + * @parameter onFailure: a zero argument callback function that will be called if the user + * cancels card scanning. + */ +CardIO.prototype.scan = function(appToken, options, onSuccess, onFailure) { + Cordova.exec(onSuccess, onFailure, "CardIOPGPlugin", "scan", [appToken, options]); +}; + +/** + * Check whether card scanning is currently available. (May vary by + * device, OS version, network connectivity, etc.) + * + * @parameter callback: a callback function accepting a boolean. + */ +CardIO.prototype.canScan = function(callback) { + var failureCallback = function() { + console.log("Could not detect whether card.io card scanning is available."); + }; + var wrappedSuccess = function(response) { + callback(response != 0); + } + Cordova.exec(wrappedSuccess, failureCallback, "CardIOPGPlugin", "canScan", []); +}; + +/** + * Retrieve the version of the card.io library. Useful when contacting support. + * + * @parameter callback: a callback function accepting a string. + */ +CardIO.prototype.version = function(callback) { + var failureCallback = function() { + console.log("Could not retrieve card.io library version"); + }; + + Cordova.exec(callback, failureCallback, "CardIOPGPlugin", "version", []); +}; + + +/** + * Plugin setup boilerplate. + */ +Cordova.addConstructor(function() { + if(!window.plugins) { + window.plugins = {}; + } + if(!window.plugins.card_io) { + window.plugins.card_io = new CardIO(); + } +}); diff --git a/iOS/card.io/CardIOPGPlugin.m b/iOS/card.io/CardIOPGPlugin.m new file mode 100644 index 0000000..6c52ef6 --- /dev/null +++ b/iOS/card.io/CardIOPGPlugin.m @@ -0,0 +1,166 @@ +// +// CardIOPGPlugin.m +// +// Copyright 2012 Lumber Labs (card.io) +// MIT licensed +// + +#import "CardIOPGPlugin.h" + +#pragma mark - + +@interface CardIOPGPlugin () + +- (void)sendSuccessTo:(NSString *)callbackId withObject:(id)objwithObject; +- (void)sendFailureTo:(NSString *)callbackId; + +@property(nonatomic, retain, readwrite) CardIOPaymentViewController *paymentViewController; +@property(nonatomic, copy, readwrite) NSString *scanCallbackId; + +@end + +#pragma mark - + +@implementation CardIOPGPlugin + +@synthesize paymentViewController; +@synthesize scanCallbackId; + +- (void)scan:(NSMutableArray *)args withDict:(NSMutableDictionary *)options { + self.scanCallbackId = [args objectAtIndex:0]; + NSString *appToken = [args objectAtIndex:1]; + + self.paymentViewController = [[[CardIOPaymentViewController alloc] initWithPaymentDelegate:self] autorelease]; + self.paymentViewController.appToken = appToken; + + NSNumber *collectCVV = [options objectForKey:@"collect_cvv"]; + if(collectCVV) { + self.paymentViewController.collectCVV = [collectCVV boolValue]; + } + + NSNumber *collectZip = [options objectForKey:@"collect_zip"]; + if(collectZip) { + self.paymentViewController.collectZip = [collectZip boolValue]; + } + + NSNumber *collectExpiry = [options objectForKey:@"collect_expiry"]; + if(collectExpiry) { + self.paymentViewController.collectExpiry = [collectExpiry boolValue]; + } + + NSNumber *disableManualEntryButtons = [options objectForKey:@"disable_manual_entry_buttons"]; + if(disableManualEntryButtons) { + self.paymentViewController.disableManualEntryButtons = [disableManualEntryButtons boolValue]; + } + + NSNumber *showsFirstUseAlert = [options objectForKey:@"shows_first_use_alert"]; + if(showsFirstUseAlert) { + self.paymentViewController.showsFirstUseAlert = [showsFirstUseAlert boolValue]; + } + + [self.viewController presentModalViewController:self.paymentViewController animated:YES]; +} + +- (void)canScan:(NSMutableArray *)args withDict:(NSMutableDictionary *)options { + NSString *callbackId = [args objectAtIndex:0]; + BOOL canScan = [CardIOPaymentViewController canReadCardWithCamera]; + [self sendSuccessTo:callbackId withObject:[NSNumber numberWithBool:canScan]]; +} + +- (void)version:(NSMutableArray *)args withDict:(NSMutableDictionary *)options { + NSString *callbackId = [args objectAtIndex:0]; + NSString *version = [CardIOPaymentViewController libraryVersion]; + + if(version) { + [self sendSuccessTo:callbackId withObject:version]; + } else { + [self sendFailureTo:callbackId]; + } +} + +- (void)dealloc { + paymentViewController.delegate = nil, [paymentViewController release], paymentViewController = nil; + [scanCallbackId release], scanCallbackId = nil; + [super dealloc]; +} + +#pragma mark - CardIOPaymentViewControllerDelegate methods + +- (void)userDidProvideCreditCardInfo:(CardIOCreditCardInfo *)info inPaymentViewController:(CardIOPaymentViewController *)pvc { + if(![pvc isEqual:self.paymentViewController]) { + NSLog(@"card.io received unexpected callback (expected from %@, received from %@", self.paymentViewController, pvc); + return; + } + + [self.paymentViewController dismissModalViewControllerAnimated:YES]; + + // Convert CardIOCreditCardInfo into dictionary for passing back to javascript + NSMutableDictionary *response = [NSMutableDictionary dictionaryWithObjectsAndKeys: + info.cardNumber, @"card_number", + info.redactedCardNumber, @"redacted_card_number", + [CardIOCreditCardInfo displayStringForCardType:info.cardType], @"card_type", + nil]; + if(info.expiryMonth > 0 && info.expiryYear > 0) { + [response setObject:[NSNumber numberWithUnsignedInteger:info.expiryMonth] forKey:@"expiry_month"]; + [response setObject:[NSNumber numberWithUnsignedInteger:info.expiryYear] forKey:@"expiry_year"]; + } + if(info.cvv.length > 0) { + [response setObject:info.cvv forKey:@"cvv"]; + } + if(info.zip.length > 0) { + [response setObject:info.zip forKey:@"zip"]; + } + + [self sendSuccessTo:self.scanCallbackId withObject:response]; + + self.paymentViewController.delegate = nil; + self.paymentViewController = nil; +} + +- (void)userDidCancelPaymentViewController:(CardIOPaymentViewController *)pvc { + if(![pvc isEqual:self.paymentViewController]) { + NSLog(@"card.io received unexpected callback (expected from %@, received from %@", self.paymentViewController, pvc); + return; + } + + [self.paymentViewController dismissModalViewControllerAnimated:YES]; + + [self sendFailureTo:self.scanCallbackId]; + + self.paymentViewController.delegate = nil; + self.paymentViewController = nil; +} + +#pragma mark - Cordova callback helpers + +- (void)sendSuccessTo:(NSString *)callbackId withObject:(id)obj { + CDVPluginResult *result = nil; + + if([obj isKindOfClass:[NSString class]]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:obj]; + } else if([obj isKindOfClass:[NSDictionary class]]) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:obj]; + } else if ([obj isKindOfClass:[NSNumber class]]) { + // all the numbers we return are bools + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsInt:[obj intValue]]; + } else if(!obj) { + result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + NSLog(@"Success callback wrapper not yet implemented for class %@", [obj class]); + } + + NSString *responseJavascript = [result toSuccessCallbackString:callbackId]; + if(responseJavascript) { + [self writeJavascript:responseJavascript]; + } +} + +- (void)sendFailureTo:(NSString *)callbackId { + CDVPluginResult *result = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + NSString *responseJavascript = [result toErrorCallbackString:callbackId]; + if(responseJavascript) { + [self writeJavascript:responseJavascript]; + } +} + +@end diff --git a/iOS/card.io/readme.md b/iOS/card.io/readme.md new file mode 100644 index 0000000..58d48fb --- /dev/null +++ b/iOS/card.io/readme.md @@ -0,0 +1,92 @@ +card.io iOS plug-in for Phone Gap +--------------------------------- + +This plug-in exposes card.io's credit card scanning. (card.io also supports charging cards; that is not yet supported in this plug-in.) + + +Integration instructions +------------------------ + +* Add the card.io library: + * Sign up for an account at https://www.card.io/, create an app, and take note of your `app_token`. + * Download the iOS SDK at https://www.card.io/integrate/ios. + * Follow the instructions there to add the requisite files, frameworks, and linker flags to your Xcode project. + +* Add this plug-in: + * Add `CardIOPGPlugin.[h|m]` to your project (Plugins group). + * Copy `CardIOPGPlugin.js` to your project's www folder using Finder or Terminal. (If you don't have a www folder yet, run in the Simulator and follow the instructions in the build warnings.) + * Add e.g. `` to your html. + * In `Cordova.plist`, add an entry to `Plugins` with key `CardIOPGPlugin` and value `CardIOPGPlugin`. + * In `Cordova.plist`, add an entry to `ExternalHosts` with value `*.card.io`. + * See `CardIOPGPlugin.js` for detailed usage information. + * Sample `canScan` usage: `window.plugins.card_io.canScan(function(canScan) {console.log("card.io can scan: " + canScan);});` + * Sample (minimal) `scan` usage: `window.plugins.card_io.scan("YOUR_APP_TOKEN", {}, function(response) {console.log("card number: " + response["card_number"]);}, function() {console.log("card scan cancelled");});` + +### Sample HTML + JS + +```html +

Scan Example

+

+ +``` + +License +------- +* This plug-in is released under the MIT license: http://www.opensource.org/licenses/MIT + +Notes +----- +* The minimum supported iOS version is iOS 4.0. +* Generic Phone Gap plug-in installation instructions are available at http://wiki.phonegap.com/w/page/43708792/How%20to%20Install%20a%20PhoneGap%20Plugin%20for%20iOS. + +Questions? Contact `support@`.