From 84e15194c9c10e87fa3417730b88b6c22009abd9 Mon Sep 17 00:00:00 2001 From: Kishan_maurya Date: Mon, 29 Apr 2024 12:22:28 +0530 Subject: [PATCH] Cashfree Card component --- example/__tests__/App-test.js | 2 +- example/__tests__/App-test.tsx | 2 +- example/index.js | 2 +- example/ios/.ruby-version | 1 + .../project.pbxproj | 15 +- .../xcschemes/CashfreePgApiExample.xcscheme | 11 +- .../xcshareddata/WorkspaceSettings.xcsettings | 5 + example/package.json | 2 +- example/{ => src}/App.js | 84 +++++- example/{ => src}/App.tsx | 119 +++++++- example/src/assests/amex.png | Bin 0 -> 911 bytes example/src/assests/diners.png | Bin 0 -> 1221 bytes example/src/assests/discover.png | Bin 0 -> 1138 bytes example/src/assests/jcb.png | Bin 0 -> 1758 bytes example/src/assests/maestro.png | Bin 0 -> 1722 bytes example/src/assests/mastercard.png | Bin 0 -> 1648 bytes example/src/assests/rupay.png | Bin 0 -> 1400 bytes example/src/assests/visa.png | Bin 0 -> 3183 bytes package.json | 4 +- src/Card/CFCardComponent.js | 197 +++++++++++++ src/Card/CFCardComponent.tsx | 263 ++++++++++++++++++ src/Card/index.js | 1 + src/Card/index.ts | 1 + src/index.js | 2 + src/index.ts | 2 + 25 files changed, 672 insertions(+), 41 deletions(-) create mode 100644 example/ios/.ruby-version create mode 100644 example/ios/CashfreePgApiExample.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename example/{ => src}/App.js (74%) rename example/{ => src}/App.tsx (77%) create mode 100644 example/src/assests/amex.png create mode 100644 example/src/assests/diners.png create mode 100644 example/src/assests/discover.png create mode 100644 example/src/assests/jcb.png create mode 100644 example/src/assests/maestro.png create mode 100644 example/src/assests/mastercard.png create mode 100644 example/src/assests/rupay.png create mode 100644 example/src/assests/visa.png create mode 100644 src/Card/CFCardComponent.js create mode 100644 src/Card/CFCardComponent.tsx create mode 100644 src/Card/index.js create mode 100644 src/Card/index.ts diff --git a/example/__tests__/App-test.js b/example/__tests__/App-test.js index c546825..8ad093c 100644 --- a/example/__tests__/App-test.js +++ b/example/__tests__/App-test.js @@ -3,7 +3,7 @@ */ import 'react-native'; import React from 'react'; -import App from '../App'; +import App from '../src/App'; // Note: test renderer must be required after react-native. import renderer from 'react-test-renderer'; it('renders correctly', () => { diff --git a/example/__tests__/App-test.tsx b/example/__tests__/App-test.tsx index 1784766..4edaa0a 100644 --- a/example/__tests__/App-test.tsx +++ b/example/__tests__/App-test.tsx @@ -4,7 +4,7 @@ import 'react-native'; import React from 'react'; -import App from '../App'; +import App from '../src/App'; // Note: test renderer must be required after react-native. import renderer from 'react-test-renderer'; diff --git a/example/index.js b/example/index.js index a850d03..69303b3 100644 --- a/example/index.js +++ b/example/index.js @@ -3,7 +3,7 @@ */ import {AppRegistry} from 'react-native'; -import App from './App'; +import App from './src/App'; import {name as appName} from './app.json'; AppRegistry.registerComponent(appName, () => App); diff --git a/example/ios/.ruby-version b/example/ios/.ruby-version new file mode 100644 index 0000000..e4604e3 --- /dev/null +++ b/example/ios/.ruby-version @@ -0,0 +1 @@ +3.2.1 diff --git a/example/ios/CashfreePgApiExample.xcodeproj/project.pbxproj b/example/ios/CashfreePgApiExample.xcodeproj/project.pbxproj index 9bfe43d..ad514ef 100644 --- a/example/ios/CashfreePgApiExample.xcodeproj/project.pbxproj +++ b/example/ios/CashfreePgApiExample.xcodeproj/project.pbxproj @@ -200,7 +200,8 @@ 83CBB9F71A601CBA00E9B192 /* Project object */ = { isa = PBXProject; attributes = { - LastUpgradeCheck = 1210; + BuildIndependentTargetsInParallel = YES; + LastUpgradeCheck = 1530; TargetAttributes = { 00E356ED1AD99517003FC87E = { CreatedOnToolsVersion = 6.2; @@ -432,6 +433,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = C5B5349BB9FA979B25D7E6E5 /* Pods-CashfreePgApiExample-CashfreePgApiExampleTests.debug.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; GCC_PREPROCESSOR_DEFINITIONS = ( "DEBUG=1", @@ -459,6 +461,7 @@ isa = XCBuildConfiguration; baseConfigurationReference = B985D2D875A593D3BD49C0E4 /* Pods-CashfreePgApiExample-CashfreePgApiExampleTests.release.xcconfig */; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; BUNDLE_LOADER = "$(TEST_HOST)"; COPY_PHASE_STRIP = NO; INFOPLIST_FILE = CashfreePgApiExampleTests/Info.plist; @@ -566,7 +569,7 @@ COPY_PHASE_STRIP = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -639,7 +642,7 @@ COPY_PHASE_STRIP = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; - "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = i386; + ENABLE_USER_SCRIPT_SANDBOXING = YES; GCC_C_LANGUAGE_STANDARD = gnu99; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -682,7 +685,7 @@ 00E356F71AD99517003FC87E /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; 13B07F931A680F5B00A75B9A /* Build configuration list for PBXNativeTarget "CashfreePgApiExample" */ = { isa = XCConfigurationList; @@ -691,7 +694,7 @@ 13B07F951A680F5B00A75B9A /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "CashfreePgApiExample" */ = { isa = XCConfigurationList; @@ -700,7 +703,7 @@ 83CBBA211A601CBA00E9B192 /* Release */, ); defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; + defaultConfigurationName = Debug; }; /* End XCConfigurationList section */ }; diff --git a/example/ios/CashfreePgApiExample.xcodeproj/xcshareddata/xcschemes/CashfreePgApiExample.xcscheme b/example/ios/CashfreePgApiExample.xcodeproj/xcshareddata/xcschemes/CashfreePgApiExample.xcscheme index 1e7dda1..b5ea366 100644 --- a/example/ios/CashfreePgApiExample.xcodeproj/xcshareddata/xcschemes/CashfreePgApiExample.xcscheme +++ b/example/ios/CashfreePgApiExample.xcodeproj/xcshareddata/xcschemes/CashfreePgApiExample.xcscheme @@ -1,6 +1,6 @@ - + + + - + + + + + diff --git a/example/package.json b/example/package.json index bd814e9..14177f3 100644 --- a/example/package.json +++ b/example/package.json @@ -13,7 +13,7 @@ "@react-native-community/checkbox": "^0.5.16", "react": "18.2.0", "react-native": "0.72.4", - "react-native-cashfree-pg-sdk": "^2.1.12" + "react-native-cashfree-pg-sdk": "^2.1.12-dev.3" }, "devDependencies": { "@babel/core": "^7.20.0", diff --git a/example/App.js b/example/src/App.js similarity index 74% rename from example/App.js rename to example/src/App.js index e9ec7ff..15b07a7 100644 --- a/example/App.js +++ b/example/src/App.js @@ -2,13 +2,14 @@ import * as React from 'react'; import { Component } from 'react'; import CheckBox from '@react-native-community/checkbox'; -import { Button, Platform, ScrollView, StyleSheet, Text, TextInput, ToastAndroid, View } from 'react-native'; -import { CFPaymentGatewayService } from 'react-native-cashfree-pg-sdk'; -import { Card, CFCardPayment, CFDropCheckoutPayment, CFEnvironment, CFPaymentComponentBuilder, CFPaymentModes, CFSession, CFThemeBuilder, CFUPI, CFUPIIntentCheckoutPayment, CFUPIPayment, SavedCard, UPIMode, } from 'cashfree-pg-api-contract'; +import { Button, Image, Platform, ScrollView, StyleSheet, Text, TextInput, ToastAndroid, View } from 'react-native'; +import { CFPaymentGatewayService, CFCard } from 'react-native-cashfree-pg-sdk'; +import { Card, CFCardPayment, CFDropCheckoutPayment, CFEnvironment, CFPaymentComponentBuilder, CFPaymentModes, CFSession, CFThemeBuilder, CFUPI, CFUPIIntentCheckoutPayment, CFUPIPayment, SavedCard, UPIMode, ElementCard } from 'cashfree-pg-api-contract'; const BASE_RESPONSE_TEXT = 'Payment Status will be shown here.'; export default class App extends Component { constructor() { super(); + this.creditCardRef = React.createRef(); this.state = { responseText: BASE_RESPONSE_TEXT, cardNumber: '', @@ -16,12 +17,13 @@ export default class App extends Component { cardExpiryMM: '', cardExpiryYY: '', cardCVV: '', - orderId: '', - sessionId: '', + orderId: 'order_342fuAjbdkqC3DVHX18iy24dP4ArK', + sessionId: 'session_Ulz4xwKmTx-4nNZTIk40n5beAXmWxMiqegtqYMcgrdsqqaxnU0mqTa15NjkAlz1M4oJcwOGyjqNQCKTEhQZAm9ekeM4VIllrICSn4t80XI3o', instrumentId: '', toggleCheckBox: false, cfEnv: '', upiId: '', + cardNetwork: require('./assests/visa.png'), }; } updateStatus = (message) => { @@ -61,6 +63,47 @@ export default class App extends Component { handleUpi = (id) => { this.setState({ upiId: id }); }; + handleCFCardInput = (data) => { + console.log('CFCardInput FROM SDK', data); + const cardNetwork = JSON.parse(data)['card_network']; + switch (cardNetwork) { + case 'visa': { + this.setState({ cardNetwork: require('./assests/visa.png') }); + break; + } + case 'mastercard': { + this.setState({ cardNetwork: require('./assests/mastercard.png') }); + break; + } + case 'amex': { + this.setState({ cardNetwork: require('./assests/amex.png') }); + break; + } + case 'maestro': { + this.setState({ cardNetwork: require('./assests/maestro.png') }); + break; + } + case 'rupay': { + this.setState({ cardNetwork: require('./assests/rupay.png') }); + break; + } + case 'diners': { + this.setState({ cardNetwork: require('./assests/diners.png') }); + break; + } + case 'discover': { + this.setState({ cardNetwork: require('./assests/discover.png') }); + break; + } + case 'jcb': { + this.setState({ cardNetwork: require('./assests/jcb.png') }); + break; + } + default: { + this.setState({ cardNetwork: require('./assests/visa.png') }); + } + } + }; componentWillUnmount() { console.log('UNMOUNTED'); CFPaymentGatewayService.removeCallback(); @@ -204,7 +247,16 @@ export default class App extends Component { getSession() { return new CFSession(this.state.sessionId, this.state.orderId, this.state.cfEnv === 'PROD' ? CFEnvironment.PRODUCTION : CFEnvironment.SANDBOX); } + handleSubmit = () => { + console.log('TYPE', this.creditCardRef); + if (this.creditCardRef.current) { + let nonPciCard = new ElementCard(this.state.cardHolderName, this.state.cardExpiryMM, this.state.cardExpiryYY, this.state.cardCVV, this.state.toggleCheckBox); + console.log('KISHANTEST', JSON.stringify(nonPciCard)); + this.creditCardRef.current.doPayment(nonPciCard); + } + }; render() { + let cfCard = React.createElement(CFCard, { cfSession: this.getSession(), style: { flex: 1 }, cardListener: this.handleCFCardInput, placeholder: 'Enter Card Number', placeholderTextColor: '#0000ff', underlineColorAndroid: 'transparent', cursorColor: 'gray', returnKeyType: 'next', ref: this.creditCardRef, onSubmitEditing: (e) => console.log('onSubmitEditing', e.nativeEvent.text, '::', e.target.value), onEndEditing: (e) => console.log('onEndEditing', e.nativeEvent.text, '::', e.target.value), onBlur: (e) => console.log('onBlur', e.nativeEvent.text, '::', e.target.value), onFocus: (e) => console.log('onFocus', e.nativeEvent.text, '::', e.target.value), onSelectionchange: (e) => console.log('onSelectionchange', e.nativeEvent.text, '::', e.target.value), onKeyPress: (e) => console.log('onSelectionchange', e.nativeEvent.text, '::', e.target.value) }); return (React.createElement(ScrollView, null, React.createElement(View, { style: styles.container }, React.createElement(View, { style: { @@ -242,18 +294,22 @@ export default class App extends Component { textAlign: 'center', marginBottom: 10, } }, + React.createElement(View, { style: styles.cardContainer }, + cfCard, + React.createElement(Image, { color: '#000', style: { + margin: 5, + }, source: this.state.cardNetwork })), React.createElement(View, { style: { flexDirection: 'column', alignSelf: 'stretch', textAlign: 'center' } }, - React.createElement(TextInput, { style: styles.input, placeholder: 'Card Number', keyboardType: 'numeric', maxLength: 16, onChangeText: this.handleCardNumber }), - React.createElement(TextInput, { style: styles.input, placeholder: 'Holder Name', keyboardType: 'default', onChangeText: this.handleCardHolderName })), + React.createElement(TextInput, { style: styles.input, placeholder: 'Holder Name', keyboardType: 'default', placeholderTextColor: '#0000ff', underlineColorAndroid: 'transparent', cursorColor: 'gray', onChangeText: this.handleCardHolderName })), React.createElement(View, { style: { flexDirection: 'row', alignSelf: 'stretch' } }, - React.createElement(TextInput, { style: styles.input, placeholder: 'Expiry Month', keyboardType: 'numeric', maxLength: 2, onChangeText: this.handleCardExpiryMM }), - React.createElement(TextInput, { style: styles.input, placeholder: 'Expiry Year', keyboardType: 'numeric', maxLength: 2, onChangeText: this.handleCardExpiryYY }), + React.createElement(TextInput, { style: styles.input, placeholder: 'Expiry Month', keyboardType: 'numeric', maxLength: 2, placeholderTextColor: '#0000ff', underlineColorAndroid: 'transparent', cursorColor: 'gray', onChangeText: this.handleCardExpiryMM }), + React.createElement(TextInput, { style: styles.input, placeholder: 'Expiry Year', keyboardType: 'numeric', maxLength: 2, placeholderTextColor: '#0000ff', underlineColorAndroid: 'transparent', cursorColor: 'gray', onChangeText: this.handleCardExpiryYY }), React.createElement(TextInput, { style: styles.input, placeholder: 'CVV', keyboardType: 'numeric', maxLength: 3, secureTextEntry: true, onChangeText: this.handleCardCVV })), React.createElement(View, { style: { flexDirection: 'row', alignSelf: 'stretch', alignItems: 'center', textAlign: 'center' } }, React.createElement(CheckBox, { value: this.state.toggleCheckBox, onValueChange: this.handleSaveCardToggle }), React.createElement(Text, null, "Saved Card for future payment")), React.createElement(View, { style: styles.button }, - React.createElement(Button, { onPress: () => this._startCardPayment(), title: 'Card Payment' }))), + React.createElement(Button, { onPress: () => this.handleSubmit(), title: 'Card Payment' }))), React.createElement(View, { style: { borderWidth: 1, alignSelf: 'stretch', @@ -289,4 +345,12 @@ const styles = StyleSheet.create({ borderWidth: 1, padding: 10, }, + cardContainer: { + flexDirection: 'row', + borderWidth: 1, + borderColor: '#000', + justifyContent: 'center', + alignItems: 'center', + margin: 10, + }, }); diff --git a/example/App.tsx b/example/src/App.tsx similarity index 77% rename from example/App.tsx rename to example/src/App.tsx index 16873ce..15d7884 100644 --- a/example/App.tsx +++ b/example/src/App.tsx @@ -1,11 +1,11 @@ // @ts-nocheck import * as React from 'react'; -import { Component } from 'react'; +import { Component, useRef } from 'react'; import CheckBox from '@react-native-community/checkbox'; -import { Button, Platform, ScrollView, StyleSheet, Text, TextInput, ToastAndroid, View } from 'react-native'; -import { CFErrorResponse, CFPaymentGatewayService } from 'react-native-cashfree-pg-sdk'; +import { Button, Image, Platform, ScrollView, StyleSheet, Text, TextInput, ToastAndroid, View } from 'react-native'; +import { CFErrorResponse, CFPaymentGatewayService, CFCard } from 'react-native-cashfree-pg-sdk'; import { Card, CFCardPayment, @@ -16,9 +16,11 @@ import { CFSession, CFThemeBuilder, CFUPI, - CFUPIIntentCheckoutPayment, CFUPIPayment, + CFUPIIntentCheckoutPayment, + CFUPIPayment, SavedCard, UPIMode, + ElementCard } from 'cashfree-pg-api-contract'; const BASE_RESPONSE_TEXT = 'Payment Status will be shown here.'; @@ -26,7 +28,7 @@ const BASE_RESPONSE_TEXT = 'Payment Status will be shown here.'; export default class App extends Component { constructor() { super(); - + this.creditCardRef = React.createRef(); this.state = { responseText: BASE_RESPONSE_TEXT, cardNumber: '', @@ -34,12 +36,13 @@ export default class App extends Component { cardExpiryMM: '', cardExpiryYY: '', cardCVV: '', - orderId: '', - sessionId: '', + orderId: 'order_342fuAjbdkqC3DVHX18iy24dP4ArK', + sessionId: 'session_Ulz4xwKmTx-4nNZTIk40n5beAXmWxMiqegtqYMcgrdsqqaxnU0mqTa15NjkAlz1M4oJcwOGyjqNQCKTEhQZAm9ekeM4VIllrICSn4t80XI3o', instrumentId: '', toggleCheckBox: false, cfEnv: '', upiId: '', + cardNetwork: require('./assests/visa.png'), }; } @@ -86,6 +89,48 @@ export default class App extends Component { this.setState({ upiId: id }); }; + handleCFCardInput = (data: string) => { + console.log('CFCardInput FROM SDK', data); + const cardNetwork = JSON.parse(data)['card_network']; + switch (cardNetwork) { + case 'visa': { + this.setState({ cardNetwork: require('./assests/visa.png') }); + break; + } + case 'mastercard': { + this.setState({ cardNetwork: require('./assests/mastercard.png') }); + break; + } + case 'amex': { + this.setState({ cardNetwork: require('./assests/amex.png') }); + break; + } + case 'maestro': { + this.setState({ cardNetwork: require('./assests/maestro.png') }); + break; + } + case 'rupay': { + this.setState({ cardNetwork: require('./assests/rupay.png') }); + break; + } + case 'diners': { + this.setState({ cardNetwork: require('./assests/diners.png') }); + break; + } + case 'discover': { + this.setState({ cardNetwork: require('./assests/discover.png') }); + break; + } + case 'jcb': { + this.setState({ cardNetwork: require('./assests/jcb.png') }); + break; + } + default: { + this.setState({ cardNetwork: require('./assests/visa.png') }); + } + } + }; + componentWillUnmount() { console.log('UNMOUNTED'); @@ -254,7 +299,31 @@ export default class App extends Component { ); } + private handleSubmit = () => { + console.log('TYPE', this.creditCardRef); + if (this.creditCardRef.current) { + let nonPciCard = new ElementCard(this.state.cardHolderName, this.state.cardExpiryMM, this.state.cardExpiryYY, this.state.cardCVV, this.state.toggleCheckBox); + console.log('KISHANTEST', JSON.stringify(nonPciCard)); + this.creditCardRef.current.doPayment(nonPciCard); + } + }; + render() { + let cfCard = console.log('onSubmitEditing')} + onEndEditing={(e) => console.log('onEndEditing')} + onBlur={(e) => console.log('onBlur')} + onFocus={(e) => console.log('onFocus')} + />; return ( @@ -329,18 +398,23 @@ export default class App extends Component { textAlign: 'center', marginBottom: 10, }}> + + {cfCard} + + - @@ -350,6 +424,9 @@ export default class App extends Component { placeholder='Expiry Month' keyboardType='numeric' maxLength={2} + placeholderTextColor='#0000ff' + underlineColorAndroid={'transparent'} + cursorColor={'gray'} onChangeText={this.handleCardExpiryMM} />