CAPITULO IV: SISTEMA DE GESTION DE SEGURIDAD Y SALUD
ACTIVIDAD PELIGROS NORMA LEGAL C OMUNICARA
#import <CommonCrypto/CommonDigest.h> #import <CommonCrypto/CommonHMAC.h> #import <CYBSCoreSDK/CYBSCoreSDK.h> #import <CYBSCoreSDK/NSDecimalNumber+Utils.h> #import "MPDemoViewController.h"
static NSString* kMetadataEncodedValue = @"RklEPUNPTU1PTi5MRy5JTkFQUC5QQVlNRU5U";
static NSString* const kPaymentSolutionDefaultValue = @"001"; static NSString* const kEnvTest = @"test";
static NSString* const kEnvLive = @"live";
static NSString* const kKeyMerchantID = @"merchantID";
static NSString* const kKeyMerchantExternalID = @"merchantExternalID"; static NSString* const kKeyTransactionKey = @"transactionKey";
static NSString* const kKeyEncryptedBlob = @"encryptedBlob"; static NSString* const kKeyEnv = @"env";
@interface MPDemoViewController () <VMposAuthenticationDelegate, VMposGatewayDelegate, UIPickerViewDataSource, UIPickerViewDelegate> @property (nonatomic, strong) VMposGateway* gateway;
@property (nonatomic, assign) BOOL authorized;
@property (nonatomic, weak) NSDictionary* selectedAccountData; @property (nonatomic, strong) NSArray* configuredAccounts; @property (weak, nonatomic) IBOutlet UISegmentedControl *requestTypeSelection;
@property (weak, nonatomic) IBOutlet UIButton *payButton; @property (weak, nonatomic) IBOutlet UITextView *statusText; @property (weak, nonatomic) IBOutlet UITextField *amountTextField; @property (weak, nonatomic) IBOutlet UIPickerView *accountPicker; @end
self.configuredAccounts = @[ @{ kKeyMerchantID: @"inapptestmerchant", kKeyMerchantExternalID: @"merchant.com.cybersource.tester", //--WARNING!---
// Transaction key should never be stored on the device or // embedded in code. The usage of the transaction key below is // shown only for demo purposes.
kKeyTransactionKey: @"Evy5pzmnlmmBNfdwbn6hvSWj32ufJt/ QO9GBipbDeLefkR0H8uzcHM/odWFRs/ ZPgjvGJNXuN7sibsFUXCjZmmLyKSNJ8zfJbXEkf4wKe57QGJuiBWZ0Lh8vewj7OiyrpJmv8Ow q9zNyNnjEbLa5IyQZRFKDffNuBYOguBVcNgaGHf6+OAt16xMqcxm0v3sohUEnhBtXq1C4aeDp A2Q7eMIOXU2o/J3hRP18AQcBdA5fPIzvoxUJg1hxcwGPZcT6ycSMCR9Pzm/ kJOh69Y3XzDYlHb2Xv1eMLINY6vNnRxZS0rZS2lYkKploz+K2jJ9z3e1fjMZt6KRPuCoHt3yz Bg==", //--NOTE---
// Encrypted blob is a Base64 of the JSON object that contains // the encrypted payment data. This blob is generated by the // PassKit API. kKeyEncryptedBlob: @"eyJkYXRhIjoiNGkwZWwxSVhMeGU1YkdjVzdWZFBZRVJ1bHhwc1p1SnRJWFVkdVY4RzBmV1R 4ZWVoWU5rYjJtRFh5RjhoWVFNclNOR1JiNzY2MU9hRGZWTjlMQXVuYnNMbG9tbTBuU0ZITCts Z1JQYjBGUFRWamN6eFVxa1A4akFEcEtwc2xtTUEwU0xRSFhcL1hYeUdTeSsxWDZBT0ltOUs0Y VBTWGt3MkNITkZaR0ErSktVdXdFbDBldlVWMnZUU0RONkZ2V1NmK0RwZUM0YlhpaUlLQ1IreE 9tWkhyc3ByTEhwNGJwcEdGYkduZ0tiTXZ0MjhIc2lNZVdIdXNLTDJ1cWNGVzdhcU5acSszZnQ 0QUxpbk1CYWJWS1ZnZjF4XC8ycitremIwcmhTQkFPWkJcLzlCNldQR3RhOWhUNmdlTERyTGMw NTNVWFZndXJOWkZ1WU1IOG1uM09MQlp2K1FmZ0dsVlRaODZYRmRMQlh4VUp2VHl3QVwvVCs1c 29KSUhJeWJNQnhiM3ZSTXR1MGhyZ2o2YUNxRWlkVWZOb0JnVEhlSDk4NStsR1BcL3lxQUk2dk pDREpCZ1dXc2dvXC9nM0prM1Q5Qzc5Vmd2UHZIbjJmOEZHYlRtS2pFVT0iLCJ2ZXJzaW9uIjo iRUNfdjEiLCJoZWFkZXIiOnsiYXBwbGljYXRpb25EYXRhIjoiOTRlZTA1OTMzNWU1ODdlNTAx Y2M0YmY5MDYxM2UwODE0ZjAwYTdiMDhiYzdjNjQ4ZmQ4NjVhMmFmNmEyMmNjMiIsInRyYW5zY WN0aW9uSWQiOiJjMWNhZjVhZTcyZjAwMzlhODJiYWQ5MmI4MjgzNjM3MzRmODViZjJmOWNhZG YxOTNkMWJhZDlkZGNiNjBhNzk1IiwiZXBoZW1lcmFsUHVibGljS2V5IjoiTUlJQlN6Q0NBUU1 HQnlxR1NNNDlBZ0V3Z2ZjQ0FRRXdMQVlIS29aSXpqMEJBUUloQVBcL1wvXC9cLzhBQUFBQkFB QUFBQUFBQUFBQUFBQUFcL1wvXC9cL1wvXC9cL1wvXC9cL1wvXC9cL1wvXC9cL01Gc0VJUFwvX C9cL1wvOEFBQUFCQUFBQUFBQUFBQUFBQUFBQVwvXC9cL1wvXC9cL1wvXC9cL1wvXC9cL1wvXC 9cLzhCQ0JheGpYWXFqcVQ1N1BydlZWMm1JYThaUjBHc014VHNQWTd6ancrSjlKZ1N3TVZBTVN kTmdpRzV3U1RhbVo0NFJPZEpyZUJuMzZRQkVFRWF4ZlI4dUVzUWtmNHZPYmxZNlJBOG5jRGZZ RXQ2ek9nOUtFNVJkaVl3cFpQNDBMaVwvaHBcL200N242MHA4RDU0V0s4NHpWMnN4WHM3THRrQ m9ONzlSOVFJaEFQXC9cL1wvXC84QUFBQUFcL1wvXC9cL1wvXC9cL1wvXC9cLys4NXZxdHB4ZW VoUE81eXNMOFl5VlJBZ0VCQTBJQUJDWU1zOWNOSCtTd2pteWJ6TWhrMmVQXC9wYUhMVm1QY25 RaHhzanNpTDBRclwvREhrTmtjOHJXSjhhVGxvRzJGVXoyM3FmU0Z1R1BEcDV4M3dlRWtleHg0 PSIsInB1YmxpY0tleUhhc2giOiJ1T25KZmUyXC9GVjdDRTlqUERIYnZJb2lHUFBWZWYzWndsd zc5bW5RSFwvekE9In0sInNpZ25hdHVyZSI6Ik1JSURRZ1lKS29aSWh2Y05BUWNDb0lJRE16Q0 NBeThDQVFFeEN6QUpCZ1VyRGdNQ0dnVUFNQXNHQ1NxR1NJYjNEUUVIQWFDQ0Fpc3dnZ0luTUl JQmxLQURBZ0VDQWhCY2wrUGYzK1U0cGsxM25WRDlud1FRTUFrR0JTc09Bd0lkQlFBd0p6RWxN Q01HQTFVRUF4NGNBR01BYUFCdEFHRUFhUUJBQUhZQWFRQnpBR0VBTGdCakFHOEFiVEFlRncwe E5EQXhNREV3TmpBd01EQmFGdzB5TkRBeE1ERXdOakF3TURCYU1DY3hKVEFqQmdOVkJBTWVIQU JqQUdnQWJRQmhBR2tBUUFCMkFHa0Fjd0JoQUM0QVl3QnZBRzB3Z1o4d0RRWUpLb1pJaHZjTkF RRUJCUUFEZ1kwQU1JR0pBb0dCQU5DOCtrZ3RnbXZXRjFPempnRE5yalRFQlJ1b1wvNU1LdmxN MTQ2cEFmN0d4NDFibEU5dzRmSVhKQUQ3RmZPN1FLaklYWU50MzlyTHl5N3hEd2JcLzVJa1pNN jBUWjJpSTFwajU1VWM4ZmQ0ZnpPcGszZnRaYVFHWE5MWXB0RzFkOVY3SVM4Mk91cDlNTW8xQl BWclhUUEhOY3NNOTlFUFVuUHFkYmVHYzg3bTByQWdNQkFBR2pYREJhTUZnR0ExVWRBUVJSTUU
rQUVIWldQcld0SmQ3WVo0MzFoQ2c3WUZTaEtUQW5NU1V3SXdZRFZRUURIaHdBWXdCb0FHMEFZ UUJwQUVBQWRnQnBBSE1BWVFBdUFHTUFid0J0Z2hCY2wrUGYzK1U0cGsxM25WRDlud1FRTUFrR 0JTc09Bd0lkQlFBRGdZRUFiVUtZQ2t1SUtTOVFRMm1GY01ZUkVJbTJsK1hnOFwvSlh2K0dCVl FKa09Lb3NjWTRpTkRGQVwvYlFsb2dmOUxMVTg0VEh3TlJuc3ZWM1BydjdSVFk4MWdxMGR0Qzh 6WWNBYUFrQ0hJSTN5cU1uSjRBT3U2RU9XOWtKazIzMmdTRTdXbEN0SGJmTFNLZnVTZ1FYOEtY UVl1WkxrMlJyNjNOOEFwWHNYd0JMM2NKMHhnZUF3Z2QwQ0FRRXdPekFuTVNVd0l3WURWUVFES Gh3QVl3Qm9BRzBBWVFCcEFFQUFkZ0JwQUhNQVlRQXVBR01BYndCdEFoQmNsK1BmMytVNHBrMT NuVkQ5bndRUU1Ba0dCU3NPQXdJYUJRQXdEUVlKS29aSWh2Y05BUUVCQlFBRWdZQm83ZXpUUmh XR2dydTBhazB6WHJ5aE1EczBhcGd0Nk9mbGhkSUE5UzBub29uZVlieVF5QTkwalF2c1FYUXcr TTZhcXhQSThNbkxOSndqVk1GdGlsSmlFODg2em9qeEhGbWdyeEZaNGN1Z2E5MjREZWRUY1RZc WtrM3g3T002YTVyTWJqYkF4UGIyQnpDZ0N4K0gyaTk3THBPSjBXQlpBQjNUTUxmcWtsUkRuQT 09In0=", //--NOTE---
// Environment setting determines the API URL endpoints. kKeyEnv: kEnvTest }, ]; self.accountPicker.delegate = self; self.accountPicker.dataSource = self; self.selectedAccountData = self.configuredAccounts[0]; } - (IBAction)payButtonTouchDown:(id)sender { [self.amountTextField resignFirstResponder];
NSString* requestType = [self.requestTypeSelection
titleForSegmentAtIndex:self.requestTypeSelection.selectedSegmentIndex]; [self updateStatusMessage:[NSString stringWithFormat:@"Submitting %@ request...", requestType]];
self.payButton.enabled = NO;
NSString *amountText = self.amountTextField.text; NSDecimalNumber *amountValue = [NSDecimalNumber decimalNumberWithString:amountText];
BOOL isDecimal = amountValue!= nil; if (isDecimal) {
// Pass in encrypted payment data from PassKit. [self performRequestWithEncryptedPaymentData:self.selectedAccountData[kKeyEncry ptedBlob] withPaymentAmount:amountValue]; } else { self.payButton.enabled = YES;
- (IBAction)requestTypeSelectionValueChanged:(UISegmentedControl *)sender {
[self updateRequestSelection]; }
-(void) updateRequestSelection {
NSString* requestType = [self.requestTypeSelection
titleForSegmentAtIndex:self.requestTypeSelection.selectedSegmentIndex]; [self updateStatusMessage:[NSString stringWithFormat:@"Tap '%@' to %@ request.", self.payButton.currentTitle, requestType]];
}
- (void) updateStatusMessage: (NSString*) message {
self.statusText.text = message; self.payButton.enabled = YES; }
- (void)performRequestWithEncryptedPaymentData: (NSString*)
encryptedPaymentData withPaymentAmount: (NSDecimalNumber*) paymentAmount {
VMposItem *item = [[VMposItem alloc] init]; item.name = NSLocalizedString(@"Item no 1", nil); item.price = paymentAmount;
VMposTransactionObject *transactionObject = [VMposTransactionObject createTransaction:VMPOS_TRANSACTION_PAYMENT];
[transactionObject addItem:item]; [transactionObject calculateTotals];
// Client application creates encrypted payment based on specification // from the Simple Order API. The following values are place holders. VMposEncryptedPayment* payment = [VMposEncryptedPayment new];
payment.encodedData = encryptedPaymentData; payment.encodedMetadata = kMetadataEncodedValue;
payment.paymentSolution = kPaymentSolutionDefaultValue; // Purchase details.
VMposPurchaseDetails* purchaseDetails = [VMposPurchaseDetails new]; purchaseDetails.partialIndicator = NO;
// Sample billing information.
VMposAddress* billTo = [VMposAddress new]; billTo.firstName = @"John"; billTo.lastName = @"Doe"; billTo.email = @"[email protected]"; billTo.street1 = @"1234 Pine St."; billTo.city = @"Redmond"; billTo.state = @"WA"; billTo.postalCode = @"98052"; billTo.country = @"US";
// Save transaction information. transactionObject.encryptedPayment = payment; transactionObject.purchaseDetails = purchaseDetails; transactionObject.purchaseDetails.commerceIndicator = @"internet"; transactionObject.transactionCode = @"ref_code_12345678"; transactionObject.billTo = billTo; // Build fingerprint. //--WARNING!---
// Fingerprint generation requires the transaction key. This should // be done on the server. It is shown here only for demo purposes. NSString* merchantID = self.selectedAccountData[kKeyMerchantID]; NSString* fingerprint = [self
buildFingerprintWithTransaction:transactionObject withMerchantId:merchantID];
NSLog(@"Fingerprint: %@", fingerprint);
VMposGateway* gateway = [VMposGateway sharedInstance];
[gateway initSessionWithUserName:merchantID withMerchantId:merchantID withFingerprint: fingerprint withDelegate:self];
if (self.selectedAccountData[kKeyEnv] == kEnvLive) {
[VMposSettings sharedInstance].cybsEnvironment = ENV_LIVE; }
else {
[VMposSettings sharedInstance].cybsEnvironment = ENV_TEST; } if (self.requestTypeSelection.selectedSegmentIndex == 0) { [gateway performAuthorizationWithTransaction:transactionObject withDelegate:self]; } else { [gateway performSaleWithTransaction:transactionObject withDelegate:self]; } } - (void)viewDidLoad { [super viewDidLoad]; [self configureAccounts];
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of resources that can be recreated. }
// Returns the number of 'columns' to display.
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView { return 1;
}
// Returns the # of rows in each component.
- (NSInteger)pickerView:(UIPickerView *)pickerView numberOfRowsInComponent:(NSInteger)component {
return self.configuredAccounts.count; }
- (NSString *)pickerView:(UIPickerView *)pickerView
titleForRow:(NSInteger)row forComponent:(NSInteger)component {
NSDictionary* rowData = self.configuredAccounts[row];
return [NSString stringWithFormat:@"%@ (%@)", rowData[kKeyMerchantID], rowData[kKeyEnv]];
}
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
self.selectedAccountData = self.configuredAccounts[row]; }
//! Callback for user session initialization.
- (void) didInitUserSession: (VMposUserSession*) paramUserSession withError:(VMposError*)paramError {
}
//! Provides feedback from finished authorization transaction request. /*!
\param paramResponseData gateway data retrieved from server (contains information about transaction status)
\param paramError an error if request failed */
- (void) authorizationFinishedWithGatewayResponse:(VMposGatewayResponse *)paramResponseData
withError:(VMposError *)paramError { self.authorized = YES;
[self updateStatusMessageWithResponse: (VMposGatewayResponse *)paramResponseData withError: paramError];
//! Provides feedback from finished sale request. /*!
\param paramResponseData gateway data retrieved from server (contains information about transaction status)
\param paramError an error if request failed */
- (void) saleFinishedWithGatewayResponse:(VMposGatewayResponse *)paramResponseData
withError:(VMposError *)paramError { self.authorized = YES;
[self updateStatusMessageWithResponse: (VMposGatewayResponse *)paramResponseData withError: paramError];
}
- (void) updateStatusMessageWithResponse: (VMposGatewayResponse *)paramResponseData withError: (NSError*) paramError
{
NSMutableString* s = [NSMutableString new]; if (paramResponseData)
{
[s appendString: @"\nTransaction Details:"];
[s appendFormat: @"\n * Accepted: %@", paramResponseData.isAccepted ? @"Yes" : @"No"];
[s appendFormat: @"\n * Auth Amount: %@", paramResponseData.authorizedAmount.stringValue]; } if (paramError) { [s appendFormat:@"\nError: %@", paramError.localizedDescription]; } [self updateStatusMessage:s]; } /* ---WARNING!---
Finger print generation requires the transaction key. This must be done on a secure server. It is shown here only for demo purposes. */
-(NSString*) buildFingerprintWithTransaction: (VMposTransactionObject*) transactionObject withMerchantId: (NSString*) merchantId {
NSDate* dateNow = [NSDate date];
NSString* fingerprintDateString = [MPDemoViewController formatFingerprintDate:dateNow];
return [NSString stringWithFormat:@"%@#%@", hashedFgComponents, fingerprintDateString];
}
+(NSString*) formatFingerprintDate: (NSDate*) date {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init]; NSTimeZone* tz = [NSTimeZone timeZoneWithName:@"UTC"];
[dateFormatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss'Z'"]; [dateFormatter setTimeZone:tz];
return [dateFormatter stringFromDate:date]; }
+ (NSString *)stringSha1:(NSString *)value {
const char *cstr = [value cStringUsingEncoding:NSUTF8StringEncoding]; NSData *data = [NSData dataWithBytes:cstr length:value.length]; uint8_t digest[CC_SHA1_DIGEST_LENGTH];
// This is an iOS5-specific method.
// It takes in the data, how much data, and then output format, which in // this case is an int array.
CC_SHA1(data.bytes, (uint)data.length, digest);
return [self stringHexEncode:digest withLength:CC_SHA1_DIGEST_LENGTH]; }
+ (NSString *)stringSha256:(NSString *)value {
const char *cstr = [value cStringUsingEncoding:NSUTF8StringEncoding]; NSData *data = [NSData dataWithBytes:cstr length:value.length]; uint8_t digest[CC_SHA256_DIGEST_LENGTH];
// This is an iOS5-specific method.
// It takes in the data, how much data, and then output format, which in // this case is an int array.
CC_SHA256(data.bytes, (uint)data.length, digest);
return [self stringHexEncode:digest withLength:CC_SHA256_DIGEST_ LENGTH];
}
+ (NSString *)stringHmacSha256:(NSString *)value {
CCHmacContext ctx;
const char* utf8ValueString = [value UTF8String]; uint8_t hmacData[CC_SHA256_DIGEST_LENGTH];
CCHmacInit(&ctx, kCCHmacAlgSHA256, utf8ValueString, strlen(utf8ValueString));
CCHmacUpdate(&ctx, utf8ValueString, strlen(utf8ValueString)); CCHmacFinal(&ctx, hmacData);
return [self stringHexEncode:hmacData withLength:CC_SHA256_DIGEST_ LENGTH];
}
+(NSString*) stringHexEncode: (uint8_t*) data withLength: (NSInteger) dataLength {
NSMutableString* output = [NSMutableString stringWithCapacity:dataLength * 2];
// Parse through the CC_SHA256 results (stored inside of digest[]). for(int i = 0; i < dataLength; i++) {
[output appendFormat:@"%02x", data[i]]; }
return output; }
+ (NSString*)base64forData:(NSData*)theData {
const uint8_t* input = (const uint8_t*)[theData bytes]; NSInteger length = [theData length];
static char table[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="; NSMutableData* data = [NSMutableData dataWithLength:((length + 2) / 3) * 4];
uint8_t* output = (uint8_t*)data.mutableBytes; NSInteger i;
for (i=0; i < length; i += 3) { NSInteger value = 0;
NSInteger j;
for (j = i; j < (i + 3); j++) { value <<= 8;
if (j < length) {
value |= (0xFF & input[j]); }
}
NSInteger theIndex = (i / 3) * 4;
output[theIndex + 0] = table[(value >> 18) & 0x3F]; output[theIndex + 1] = table[(value >> 12) & 0x3F];
output[theIndex + 2] = (i + 1) < length ? table[(value >> 6) & 0x3F] : '=';