Merge pull request #439 from card-io/add-card-io

Add card.io iOS plugin
This commit is contained in:
Randy McMillan
2012-04-03 17:41:15 -07:00
4 changed files with 362 additions and 0 deletions

View File

@@ -0,0 +1,26 @@
//
// CardIOPGPlugin.h
//
// Copyright 2012 Lumber Labs (card.io)
// MIT licensed
//
#import <Foundation/Foundation.h>
#import "CardIO.h"
#ifdef CORDOVA_FRAMEWORK
#import <Cordova/CDVPlugin.h>
#else
#import "CDVPlugin.h"
#endif
@interface CardIOPGPlugin : CDVPlugin<CardIOPaymentViewControllerDelegate> {
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

View File

@@ -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();
}
});

View File

@@ -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

92
iOS/card.io/readme.md Normal file
View File

@@ -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. `<script type="text/javascript" charset="utf-8" src="CardIOPGPlugin.js"></script>` 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
<h1>Scan Example</h1>
<p><button id='scanBtn'>Scan now</button></p>
<script type="text/javascript">
function onDeviceReady() {
var cardIOResponseFields = [
"card_type",
"redacted_card_number",
"card_number",
"expiry_month",
"expiry_year",
"cvv",
"zip"
];
var onCardIOComplete = function(response) {
console.log("card.io scan complete");
for (var i = 0, len = cardIOResponseFields.length; i < len; i++) {
var field = cardIOResponseFields[i];
console.log(field + ": " + response[field]);
}
};
var onCardIOCancel = function() {
console.log("card.io scan cancelled");
};
var onCardIOCheck = function (canScan) {
console.log("card.io canScan? " + canScan);
var scanBtn = document.getElementById("scanBtn");
if (!canScan) {
scanBtn.innerHTML = "Manual entry";
}
scanBtn.onclick = function (e) {
window.plugins.card_io.scan(
"YOUR_APP_TOKEN_HERE",
{
"collect_expiry": true,
"collect_cvv": false,
"collect_zip": false,
"shows_first_use_alert": true,
"disable_manual_entry_buttons": false
},
onCardIOComplete,
onCardIOCancel
);
}
};
window.plugins.card_io.canScan(onCardIOCheck);
}
</script>
```
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@<the obvious domain>`.