A Payment Card UI & Validator for iOS

Related tags

Payments Caishen
Overview

Caishen

Travis build status Cocoapods Compatible Carthage compatible Platform Docs

Description

Caishen provides an easy-to-use text field to ask users for payment card information and to validate the input. It serves a similar purpose as PaymentKit, but is developed as a standalone framework entirely written in Swift. Caishen also allows an easy integration with other third-party frameworks, such as CardIO.

![Caishen example](caishen_example.gif)

Requirements

  • iOS 8.0+
  • Xcode 10.0+

Installation

CocoaPods

Caishen is available through CocoaPods. To install it, simply add the following line to your Podfile:

pod "Caishen"

Carthage

Caishen is available through Carthage. To install it, simply add the following line to your Cartfile:

github "prolificinteractive/Caishen"

Usage

Example

To run the example project, clone the repo, and run pod install from the Example directory first.


Inside your project

To add a text field for entering card information to a view, either ...

  • ... add a UITextField to your view in InterfaceBuilder and change its class to CardTextField (when using InterfaceBuilder)
  • ... or initiate a CardTextField with one of its initializers (when instantiating from code):
    • init?(coder: aDecoder: NSCoder)
    • init(frame: CGRect)

To get updates about entered card information on your view controller, confirm to the protocol CardTextFieldDelegate set the view controller as cardTextFieldDelegate for the text field:

class MyViewController: UIViewController, CardTextFieldDelegate {
	
	@IBOutlet weak var cardTextField: CardTextField?
	
	override func viewDidLoad() {
		cardTextField?.cardTextFieldDelegate = self
		
		...
	}
	
	func cardTextField(_ cardTextField: CardTextField, didEnterCardInformation information: Card, withValidationResult validationResult: CardValidationResult) {
        // A valid card has been entered, if validationResult == CardValidationResult.Valid
    }
    
    func cardTextFieldShouldShowAccessoryImage(_ cardTextField: CardTextField) -> UIImage? {
        // You can return an image which will be used on cardTextField's accessory button
		 // If you return nil but provide an accessory button action, the unicode character "⇤" is displayed instead of an image to indicate an action that affects the text field.
    }
    
    func cardTextFieldShouldProvideAccessoryAction(_ cardTextField: CardTextField) -> (() -> ())? {
		 // You can return a callback function which will be called if a user tapped on cardTextField's accessory button
		 // If you return nil, cardTextField won't display an accessory button at all.
    }
	
	...

Customizing the text field appearance

CardTextField is mostly customizable like every other UITextField. Setting any of the following standard attributes for a CardTextField (either from code or from interface builder) will affect the text field just like it affects any other UITextField:

Property Type Description
placeholder String? The card number place holder. When using a card number as placeholder, make sure to format it appropriately so it uses the cardNumberSeparator that has been set for the text field (i.e. when using " - " as separator, set a placeholder like "1234 - 1234 - 1234 - 1234").
textColor UIColor? The color of text entered into the CardTextField.
backgroundColor UIColor? The background color of the text field.
font UIFont? The font of the entered text.
secureTextEntry Bool When set to true, any input in the text field will be secure (i.e. masked with "•" characters).
keyboardAppearance UIKeyboardAppearance The keyboard appearance when editing text in the text field.
borderStyle UITextBorderStyle The border style for the text field.

Additionally, CardTextField offers attributes tailored to its purpose (accessible from interface builder as well):

Property Type Description
cardNumberSeparator String? A string that is used to separate the groups in a card number. Defaults to " - ".
viewAnimationDuration Double? The duration for a view animation in seconds when switching between the card number text field and details (month, view and cvc text fields).
invalidInputColor UIColor? The text color for invalid input. When entering an invalid card number, the text will flash in this color and in case of an expired card, the expiry will be displayed in this color as well.

CardIO

CardIO might be among the most powerful tools to let users enter their payment card information. It uses the camera and lets the user scan his or her credit card. However, you might still want to provide users with a visually appealing text field to enter their payment card information, since users might want to restrict access to their camera or simply want to enter this information manually.

In order to provide users with a link to CardIO, you can use a CardTextField's prefillCardInformation method alongside the previously mentioned accessory button:

// 1. Let your view controller confirm to the CardTextFieldDelegate and CardIOPaymentViewControllerDelegate protocol:
class ViewController: UIViewController, CardTextFieldDelegate, CardIOPaymentViewControllerDelegate {
	...
	
	// MARK: - CardTextFieldDelegate
    func cardTextField(_ cardTextField: CardTextField, didEnterCardInformation information: Card, withValidationResult validationResult: CardValidationResult) {
        if validationResult == .Valid {
        	// A valid payment card has been manually entered or CardIO was used to scan one.
        }
    }
    
    // 2. Optionally provide an image for the CardIO button
    func cardTextFieldShouldShowAccessoryImage(_ cardTextField: CardTextField) -> UIImage? {
        return UIImage(named: "cardIOIcon")
    }
    
    // 3. Set the action for the accessoryButton to open CardIO:
    func cardTextFieldShouldProvideAccessoryAction(_ cardTextField: CardTextField) -> (() -> ())? {
        return { [weak self] _ in
            let cardIOViewController = CardIOPaymentViewController(paymentDelegate: self)
            self?.presentViewController(cardIOViewController, animated: true, completion: nil)
        }
    }
    
    // MARK: - CardIOPaymentViewControllerDelegate
    
    // 4. When receiving payment card information from CardIO, prefill the text field with that information:
    func userDidProvideCreditCardInfo(cardInfo: CardIOCreditCardInfo!, inPaymentViewController paymentViewController: CardIOPaymentViewController!) {
        cardTextField.prefillCardInformation(
        	cardInfo.cardNumber, 
        	month: Int(cardInfo.expiryMonth), 
        	year: Int(cardInfo.expiryYear), 
        	cvc: cardInfo.cvv)
        	
        paymentViewController.dismissViewControllerAnimated(true, completion: nil)
    }
    
    func userDidCancelPaymentViewController(paymentViewController: CardIOPaymentViewController!) {
        paymentViewController.dismissViewControllerAnimated(true, completion: nil)
    }
}

Specifying your own card types

CardTextField further contains a CardTypeRegister which maintains a set of different card types that are accepted by this text field. You can create your own card types and add or remove them to or from card number text fields:

struct MyCardType: CardType {
    
    // MARK: - Required

    // The name of your specified card type:
    public let name = "My Card Type"

    // Note: The image that will be displayed in the card number text field's image view when this card type has been detected will load from an asset with the name `cardType.name`.

    // If the Issuer Identification Number (the first six digits of the entered card number) of a card number 
    // starts with anything from 1000 to 1111, the card is identified as being of type "MyCardType":
    public let identifyingDigits = Set(1000...1111)

    // The number of digits expected in the Card Validation Code.
    public let CVCLength = 4

    // MARK: - Optional

    // The grouping of your card number type. The following results in a card number format
    // like "100 - 0000 - 00000 - 000000":
    // Not specifying this will result in a 16 digit number, separated into 4 groups of 4 digits.
    public let numberGrouping = [3, 4, 5, 6]

    /** 
     A boolean flag that indicates whether CVC validation is required for this card type or not.
     Setting this value to false will hide the CVC text field from the `CardTextField` and remove the required validation routine. The default is true.
     */
    public let requiresCVC = true

    /**
     A boolean flag that indicates whether expiry validation is required for this card type or not.
     Setting this value to false will hide the month and year text field from the `CardTextField` and remove the required
     validation routine. The default is true.
     */
    public let requiresExpiry = true
	
    public init() {
		
    }
}

...

class MyViewController: UIViewController, CardTextFieldDelegate {
	
	@IBOutlet weak var cardTextField: CardTextField?
	...
	
	func viewDidLoad() {
		cardTextField?.cardTypeRegister.registerCardType(MyCardType())
	}
	
	...
}

Using the different components of the text field separately

Instead of entering the card number, expiry and CVC in a single text field, it is possible to use single text fields to enter this information separately.

class ViewController: UIViewController, NumberInputTextFieldDelegate, CardInfoTextFieldDelegate {
    
    @IBOutlet weak var cardNumberTextField: NumberInputTextField!
    @IBOutlet weak var monthInputTextField: MonthInputTextField!
    @IBOutlet weak var yearInputTextField: YearInputTextField!
    @IBOutlet weak var cvcInputTextField: CVCInputTextField!
        
    // A card that is not nil when valid information has been entered in the text fields:
    var card: Card? {
        let number = cardNumberTextField.cardNumber
        let cvc = CVC(rawValue: cvcInputTextField.text ?? "")
        let expiry = Expiry(month: monthInputTextField.text ?? "", year: yearInputTextField.text ?? "")
                        ?? Expiry.invalid
        
        let cardType = cardNumberTextField.cardTypeRegister.cardTypeFor(number: cardNumberTextField.cardNumber)
        if cardType.validate(cvc: cvc).union(cardType.validate(expiry: expiry)).union(cardType.validate(number: number)) == .Valid {
            return Card(number: number, cvc: cvc, expiry: expiry)
        } else {
            return nil
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        cardNumberTextField.numberInputTextFieldDelegate = self
        monthInputTextField.cardInfoTextFieldDelegate = self
        yearInputTextField.cardInfoTextFieldDelegate = self
        cvcInputTextField.cardInfoTextFieldDelegate = self
        
        // Set the `deleteBackwardCallbacks` - closures which are called whenever a user hits
        // backspace on an empty text field.
        monthInputTextField.deleteBackwardCallback = { _ in self.cardNumberTextField.becomeFirstResponder() }
        yearInputTextField.deleteBackwardCallback = { _ in self.monthInputTextField.becomeFirstResponder() }
        cvcInputTextField.deleteBackwardCallback = { _ in self.yearInputTextField.becomeFirstResponder() }
    }
    
    func numberInputTextFieldDidComplete(_ numberInputTextField: NumberInputTextField) {
        cvcInputTextField.cardType = numberInputTextField.cardTypeRegister.cardTypeFor(number: numberInputTextField.cardNumber)
        print("Card number: \(numberInputTextField.cardNumber)")
        print(card)
        monthInputTextField.becomeFirstResponder()
    }
    
    func numberInputTextFieldDidChangeText(_ numberInputTextField: NumberInputTextField) {
        
    }
    
    func textField(_ textField: UITextField, didEnterValidInfo: String) {
        switch textField {
        case is MonthInputTextField:
            print("Month: \(didEnterValidInfo)")
            yearInputTextField.becomeFirstResponder()
        case is YearInputTextField:
            print("Year: \(didEnterValidInfo)")
            cvcInputTextField.becomeFirstResponder()
        case is CVCInputTextField:
            print("CVC: \(didEnterValidInfo)")
        default:
            break
        }
        print(card)
    }
    
    func textField(_ textField: UITextField, didEnterPartiallyValidInfo: String) {
        // The user entered information that is not valid but might become valid on further input.
        // Example: Entering "1" for the CVC is partially valid, while entering "a" is not.
    }
    
    func textField(_ textField: UITextField, didEnterOverflowInfo overFlowDigits: String) {
        // This function is used in a CardTextField to carry digits to the next text field.
        // Example: A user entered "02/20" as expiry and now tries to append "5" to the month.
        //          On a card text field, the year will be replaced with "5" - the overflow digit.
    }
    
    // ...
}

Contributing to Caishen

To report a bug or enhancement request, feel free to file an issue under the respective heading.

If you wish to contribute to the project, fork this repo and submit a pull request. Code contributions should follow the standards specified in the Prolific Swift Style Guide.

License

Caishen is Copyright (c) 2017 Prolific Interactive. It may be redistributed under the terms specified in the LICENSE file.

Maintainers

prolific

Caishen is maintained and funded by Prolific Interactive. The names and logos are trademarks of Prolific Interactive.

Comments
  • Added proper right-to-left language support.

    Added proper right-to-left language support.

    Implemented different behavior for the card text field when running in Right-to-left language environment.

    • Modified view animation to crop the card number to the last four digits when RTL.
    • Inverted reading of card type image, card number, month, year, CVC and accessory button when RTL.
    • To test this when running on simulator in a left-to-right language, you can do the following:
    1. Clone the Caishen_Example scheme.
    2. Edit the cloned scheme: Run ‣ Options ‣ Application Language - change that to Right to Left Pseudolanguage.
    3. Run that scheme.

    @mohammad19991

    opened by DannyVancura 9
  • xcode 9 beta 4 Build failed pod

    xcode 9 beta 4 Build failed pod

    StringExtension.swift

        func NSRangeFrom(_ range : Range<String.Index>) -> NSRange {
            let utf16view = self.utf16
            let from = String.UTF16View.Index(range.lowerBound, within: utf16view)
            let to = String.UTF16View.Index(range.upperBound, within: utf16view)
            return NSMakeRange(utf16view.startIndex.distance(to: from), from.distance(to: to))
        }
    

    NSMakeRange(utf16view.startIndex.distance(to: from), from.distance(to: to)) Value of optional type 'String.UTF16View.Index?' (aka 'Optional<String.Index>') not unwrapped; did you mean to use '!' or '?'?

    opened by feemi 6
  • Fix Placeholders

    Fix Placeholders

    Currently the library is trying to draw placeholders twice. The empty function drawPlaceholders rewrites the placeholder to empty. This PR removes the drawing over

    opened by saltyskip 5
  • Allow to disable Luhn test for a custom card type

    Allow to disable Luhn test for a custom card type

    Hi, Would it be possible to have the option to disable Luhn test in a custom card type? Some cards don't require this validation but Caishen doesn't allow this functionality currently.

    I would suggest to create a boolean in CardType that is checked before Luhn test is applied.

    Thanks!

    opened by ThibaultKlein 5
  • Error on installing on swift 3

    Error on installing on swift 3

    Hi,

    I tried my best before posting my answer here ... but still did not work ..

    pod 'Caishen', :git => 'https://github.com/prolificinteractive/Caishen.git', :branch => 'swift-3.0'

    but I receive this error: [!] Error installing Caishen [!] /usr/bin/git clone https://github.com/prolificinteractive/Caishen.git /var/folders/3f/hlddm2z55_91b6hx68g2vflw0000gn/T/d20161229-86999-1vt21f1 --template= --single-branch --depth 1 --branch swift-3.0

    Cloning into '/var/folders/3f/hlddm2z55_91b6hx68g2vflw0000gn/T/d20161229-86999-1vt21f1'... warning: Could not find remote branch swift-3.0 to clone. fatal: Remote branch swift-3.0 not found in upstream origin

    opened by albarq 4
  • Rename animation methods and change access to public.

    Rename animation methods and change access to public.

    Renamed the animation methods to be more descriptive and changed the access to public in order to let developers manually call these methods when desired.

    Resolves #55

    opened by DannyVancura 4
  • Support new BIN ranges for MasterCard

    Support new BIN ranges for MasterCard

    MasterCard just introduced new BIN ranges. Caishen should be able to recognize new ranges as a MasterCard

    https://www.cardfellow.com/new-mastercard-bins/

    opened by hkellaway 3
  • Valid Discover card displayed as invalid

    Valid Discover card displayed as invalid

    Using valid Discover card test number: 6229 2567 3724 8989

    Caishen UI displays as invalid. Simply asking Caishen to do a Luhn check on it, passes though 🤔

    opened by hkellaway 3
  • Can't type anything into the custom TextFields.

    Can't type anything into the custom TextFields.

    I just set my TextFields to correspond to the examples (e.g. CVCInputTextField) and when I try to type inside those, nothing happens (cursor doesn't move and I can't see the text). Moreover, the keyboard doesn't change to the corresponding input type.

    If I try to type something in other non-custom Text Fields inside my UI, it works.

    opened by teo029 3
  • Validate Card on pre-fill

    Validate Card on pre-fill

    After pre-filling fields with CardIO, didEnterCardInformation is not called. Some actions are taken after a successful validation, such as enabling a "Submit" button.

    How would I force textfield to validate and call the didEnterCardInformation delegate method?

    opened by BeauNouvelle 3
  • new to library

    new to library

    i'm writing an app using swift 3

    i added this framework to my project using carthage. everything appears to be fine.
    however in interface builder, i can't set the class to CardTextField

    I get an error when running the application Unknown class CardTextField in Interface Builder file

    any help would be appreciated

    opened by Nathan187 3
  • CardIO is archived, move away from CardIO in docs

    CardIO is archived, move away from CardIO in docs

    CardIO is archived project. It cannot be relied upon. To remain relevant, Caishen should drop support and supply other approach in docs not using it.

    https://github.com/card-io/card.io-iOS-SDK

    opened by bricepollock 0
  • The last four digits of the credit card are not shown in the text field when prefill is used

    The last four digits of the credit card are not shown in the text field when prefill is used

    If I enter a valid credit card number, the text scrolls to the left to allow me to enter the CVC and the expiry date. In this instance, the last four digits of the credit card are shown on the left. Screenshot 2019-10-12 at 1 56 59 PM

    However, if the prefill method is used, the last four digits of the credit card are not shown on the left side. Screenshot 2019-10-12 at 1 59 44 PM

    opened by ghost 0
  • Fix memory leak and fix crash

    Fix memory leak and fix crash

    Remove the overrides to becomeFirstResponder, resignFirstResponder, and isFirstResponder as they are causing UIKit to behave erroneously by creating a strong reference to the CardTextField that is not being released. Please see Issue #151 for details and reproducible steps.

    My theory is that UIKit is having trouble that both the CardTextField and the child text field (NumberInputTextField for example) were both claiming to be the first responder at the same time.

    Note that this could potentially be a breaking change if someone was relying on the logic in these overriden functions.

    opened by classic-chris 0
  • Memory Leak and Crash

    Memory Leak and Crash

    If the user taps the CardTextField and then pops back from the view controller the expectation is that the CardTextField will be removed from memory because the view controller (or storyboard) should have held the only strong reference to the text field.

    Instead the CardTextField remains in memory because a strong reference is retained by the UIKeyboardImpl.

    retained_textfield

    The only way to release this is for the user to tap a different text field which causes the UIKeyboardImpl to release its reference to CardTextField.

    This leak can cause a crash if the user inputs their card number and their expiry month, then taps backspace until the month is cleared but the cursor is still in the month field. Then pop back on the navigation controller, re-enter the screen with the CardTextField and tap in the text field. See the gif below for the exact steps to reproduce:

    crash_steps

    Here is the sample project to reproduce: CaishenBug.zip

    opened by classic-chris 1
  • Variable length validation for cards

    Variable length validation for cards

    Hi there, I'm currently integrating this library into my app, and it's working really well so far. However, one potential issue that I've got is that I do not see any way of handling variable length card numbers (i.e., the VISA spec states that VISA card numbers can be between 13 - 19 digits, however the standard Caishen validation assumes 16 for VISA).

    I was wondering if anyone is aware if there is any way of supporting this in the library, and whether this is something that we should even be supporting? I was alerted to the fact that this isn't handled as some of my test cards are 13 digits.

    Any ideas on this would be really useful.

    opened by MalcolmScoffable 2
Releases(2.2)
Owner
Prolific Interactive
Prolific Interactive is a strategy-led mobile agency partnering with high-growth lifestyle brands.
Prolific Interactive
card.io provides fast, easy credit card scanning in mobile apps

card.io SDK for iOS card.io provides fast, easy credit card scanning in mobile apps. NEW!!! card.io is now an open-source project! As of December 2014

card.io 2.3k Jan 4, 2023
Card Decks is a small utility application for your iPhone, iPod touch and iPad which brings you simple, configurable, colored, multi-line text cards that are grouped into card decks

Card Decks is a small utility application for your iPhone, iPod touch and iPad which brings you simple, configurable, colored, multi-line text cards that are grouped into card decks.

Arne Harren 40 Nov 24, 2022
Payment Portal Interaction

PaymentInteraction Payment Portal Interaction This Interaction was built keeping in mind Usability,Clean UI,Natural Response. Libraries Used Hero-iOS

Utkarsh Dixit 1 Oct 1, 2021
Checkout API Client, Payment Form UI and Utilities in Swift

Frames iOS Requirements iOS 10.0+ Xcode 12.4+ Swift 5.3+ Documentation Further information on using the Frames SDK is available in the integration gui

Checkout.com 48 Jan 5, 2023
Better payment user experience library with cool animation in Swift

?? Preview ?? Features Easily usable Simple Swift syntax Cool flip animation Compatible with Carthage Compatible with CocoaPods Customizable Universal

yassir RAMDANI 177 Nov 20, 2022
With SwiftUI payment share app

Splitpayment With SwiftUI payment share app.

Ahmet Onur Şahin 3 Apr 18, 2022
Easy to use iOS library with components for input of Credit Card data.

AnimatedCardInput This library allows you to drop into your project two easily customisable, animated components that will make input of Credit Card i

Netguru 39 Oct 16, 2022
Easily integrate Credit Card payments module in iOS App. Swift 4.0

MFCard Buy me a coffee MFCard is an awesome looking Credit Card input & validation control. Written in Swift 3. YOoo, Now MFCard is on Swift 5. Swift

MobileFirst 362 Nov 29, 2022
A credit card reader and parser for iOS Using Native Vision/VisionKit

card-reader-ios A credit card reader and parser for iOS Using Native Vision/VisionKit May-14-2021.00-43-17.mp4 Instructions Hold camera up to a card a

Khalid Asad 104 Dec 15, 2022
CreditCardForm is iOS framework that allows developers to create the UI which replicates an actual Credit Card.

CreditCardForm CreditCardForm is iOS framework that allows developers to create the UI which replicates an actual Credit Card. Fixed typo use CreditCa

Orazz 1.4k Dec 15, 2022
A credit card scanner for iOS written in Swift

DGCardScanner A credit card scanner Requirements iOS 13.0+ Swift 5.5+ Xcode 10.0+ Installation SPM File > Add Packages > https://github.com/donggyushi

donggyu 9 Jun 24, 2022
SwiftUI BusinessCard - Created iOS Business card app to practice SwiftUI

SwiftUI_BusinessCard Created iOS Business card app to practice SwiftUI

null 0 Jan 29, 2022
Bank Card Generator with Swift using SnapKit DSL 💳

iCard BankCard & CreditCard generator with Swift 3 using SnapKit DSL iCard is a simple tool for generate Credit & Bank Card , it represent cards as UI

Emrah Korkmaz 334 Nov 28, 2022
Debit/Credit card validation port of the Luhn Algorithm in Swift

SwiftLuhn Warning! This repository is no longer maintained. This is a port of the Luhn Algorithm, generally used for validating debit/credit card deta

Max Kramer 135 Sep 9, 2022
Luhn Credit Card Validation Algorithm

Luhn Algorithm This is a port of the Luhn Algorithm, generally used for validating Credit Card details, to Objective-C (iOS). Swift port can be found

Max Kramer 127 Nov 27, 2022
Welcome Busines Card Built With Swift

BusinessCard Welcome Busines Card Main screen Contacts screen More info screen

Konstantin Ryabtsev 0 Dec 28, 2021
A card viewer demo for Idolmaster Millionlive Theater Days written in Swift UI

Misaki Gallery A Millionlive's cards gallery to demostrate Swift UI. All api and

Spike 0 Dec 20, 2021
A study card application built with SwiftUI and Combine

StudyCards Description A study card application. I built this application to get

Matt Manion 2 Sep 26, 2022
TextFieldFormatter - Simple Text Formatter (Credit Card Number, Phone Number, Serial Number etc.)

TextFieldFormatter Simple Text Formatter (Credit Card Number, Phone Number, Seri

Anıl ORUÇ 4 Apr 4, 2022