Skip to content

Commit

Permalink
Merge pull request #8 from Allmacamo/feature/payfast-hook
Browse files Browse the repository at this point in the history
Feature/payfast hook
  • Loading branch information
Allmacamo committed Jun 16, 2023
2 parents 0e96d59 + dce978c commit 7bee58c
Show file tree
Hide file tree
Showing 7 changed files with 221 additions and 10 deletions.
110 changes: 102 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,22 @@ This is a React Native package for integration of Payfast payment gateway into y

## Features

- Make a once off payment for any product/service within the app
- Easy to use package for integrating Payfast payment gateway into your React Native app
- Written in TypeScript, providing type definitions and improved developer experience
- Compatible with React Native & React Native Expo
- Make once off or repeat payments using a bank card e.g Cheque or Credit Card
- Save bank card for future use (Tokenization), token sent to your notifyUrl
- Update saved bank card details
- Hook/Function to make a payment with saved card / token

## Upcoming Features

- Recurring Billing / Subscriptions

## About the package

- Make a once off payment for any product/service within the app
- Easy to use package for integrating Payfast payment gateway into your React Native app
- Written in TypeScript, providing type definitions and improved developer experience
- Compatible with React Native & React Native Expo

## Props

### Paystack Props
Expand Down Expand Up @@ -68,25 +75,25 @@ Options
Install the latest version of the package
Using Yarn

```
```bash
yarn add react-native-payfast-checkout
```

or Npm

```
```bash
npm install react-native-payfast-checkout
```

or Expo

```
```bash
npx expo install react-native-payfast-checkout
```

Use the package in your cart or checkout screen

```
```tsx
...
import Payfast from 'react-native-payfast-checkout'
...
Expand Down Expand Up @@ -116,6 +123,93 @@ const CartScreen = () => {
export default CartScreen
```

Adding saving a card for future use

Unfortunately Payfast does not return any details for your saved card, except for the token, therefore you can't show the last 4 digits for the user to remember the card they saved, but the update card screen shows the last 4 digits and the expiry date

```tsx
...
import {PayFastSaveCard} from 'react-native-payfast-checkout'
...
const PaymentOptionsScreen = () => {

const [showAddCard, setShowAddCard] = useState(false)
const [showUpdateCard, setShowUpdateCard] = useState(false)

const transactionDetails = {
customerFirstName: 'John',
customerLastName: 'Doe',
customerEmailAddress: 'name@email.com',
}

return <View>
...
<PayFastSaveCard
transactionDetails={transactionDetails}
merchantId={process.env.PAYFAST_MERCHANT_ID}
merchantKey={process.env.PAYFAST_MERCHANT_KEY}
passPhrase={process.env.PAYFAST_SIGNATURE_PHRASE}
notifyUrl=''
isVisible={showAddCard}
onClose={(isDone) => {
// Do things here then
setShowAddCard(false)
}}
/>
...
<PayFastUpdateCard
cardToken=''
isVisible={showUpdateCard}
sandbox
onClose={() => {
// Do things here
setShowUpdateCard(false)
}}
/>
...
</View>
}

export default PaymentOptionsScreen
```

Using a token in a transaction

```tsx
...
import { usePayFast } from 'react-native-payfast-checkout'
...

const CheckoutScreen = () => {
...
const { chargeCardToken } = usePayFast({
merchantId:{process.env.PAYFAST_MERCHANT_ID}
passPhrase:{process.env.PAYFAST_SIGNATURE_PHRASE}
})
...
const onCheckout = async( ) => {
try {
const { message, pf_payment_id } = await chargeCardToken({
token:'',
total:10000 // in cents e.g 10000 = R100,
reference:'',
itemName:''
})
} catch(error){
console.log({error})
}
}
...
return <View>
...
<Button text='Checkout' onPress={() => onCheckout() } />
...
</View>
}

export default CheckoutScreen
```

### Official Documentation

For more details you can visit the official Payfast [documentation page](https://developers.payfast.co.za/docs#home)
Expand Down
4 changes: 3 additions & 1 deletion index.tsx → index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import PayFast from './src/Payfast'
import PayFastSaveCard from './src/SaveCard'
import PayFastUpdateCard from './src/UpdateCard'
import usePayFast from './src/hooks'
import {
PayFastMerchantDetails,
PayFastSubscriptionDetails,
Expand All @@ -14,6 +15,7 @@ export {
PayFastSubscriptionDetails,
PayFastSubscriptionFrequency,
PayFastTransactionDetails,
PayFastUpdateCard
PayFastUpdateCard,
usePayFast
}
export default PayFast
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
"payment-gateway-api"
],
"dependencies": {
"axios": "^1.4.0",
"date-fns": "^2.30.0",
"react-native-webview": "^11.18.1"
},
"peerDependencies": {
Expand Down
63 changes: 63 additions & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import axios from 'axios'
import format from 'date-fns/format'
import { buildQueryString, generateMD5 } from '../Helpers'
import { ChargeCardToken } from '../types'

type Props = {
version?: string
merchantId: string
passphrase: string
sandbox?: boolean
}

const usePayFast = ({ version, merchantId, passphrase, sandbox }: Props) => {
const chargeCardToken = async ({ token, total, reference, itemName }: ChargeCardToken) => {
try {
const payload = {
amount: total,
item_name: itemName,
m_payment_id: reference,
'merchant-id': merchantId,
passphrase,
timestamp: format(new Date(), 'yyyy-MM-dd HH:mm:ss').replace(' ', 'T'),
version: version || 'v1'
}
const string = buildQueryString(payload)
const signature = generateMD5(string)

let url = `https://api.payfast.co.za/subscriptions/${token}/adhoc`
if (sandbox) {
url += '?testing=true'
}

const { data } = await axios.post(
url,
{
amount: payload.amount,
item_name: payload.item_name,
m_payment_id: payload.m_payment_id
},
{
headers: {
'merchant-id': merchantId,
version: version,
timestamp: payload.timestamp,
signature
}
}
)
if (data.data.message.includes('Failure')) {
throw new Error(data.data.response)
}
return data.data
} catch (error) {
throw new Error(error)
}
}

return {
chargeCardToken
}
}

export default usePayFast
8 changes: 8 additions & 0 deletions src/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,11 @@ export type PayFastMerchantDetails = {
merchantKey?: string
passPhrase?: string
}

export type ChargeCardToken = {
token: string
total: number
itemName: string
itemDescription?: string
reference: string
}
Binary file added updateCard.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
44 changes: 43 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -700,6 +700,13 @@
dependencies:
regenerator-runtime "^0.13.11"

"@babel/runtime@^7.21.0":
version "7.22.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.22.5.tgz#8564dd588182ce0047d55d7a75e93921107b57ec"
integrity sha512-ecjvYlnAaZ/KVneE/OdKYBYfgXV3Ptu6zQWmgEF7vwKhQnvVS6bjMD2XYgj+SNvQ1GfK/pjgokfPkC/2CO8CuA==
dependencies:
regenerator-runtime "^0.13.11"

"@babel/template@^7.0.0", "@babel/template@^7.18.10", "@babel/template@^7.20.7":
version "7.20.7"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.20.7.tgz#a15090c2839a83b02aa996c0b4994005841fd5a8"
Expand Down Expand Up @@ -2062,6 +2069,15 @@ aws4@^1.8.0:
resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.12.0.tgz#ce1c9d143389679e253b314241ea9aa5cec980d3"
integrity sha512-NmWvPnx0F1SfrQbYwOi7OeaNGokp9XhzNioJ/CSBs8Qa4vxug81mhJEAVZwxXuBmYB5KDRfMq/F3RR0BIU7sWg==

axios@^1.4.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
dependencies:
follow-redirects "^1.15.0"
form-data "^4.0.0"
proxy-from-env "^1.1.0"

babel-core@^7.0.0-bridge.0:
version "7.0.0-bridge.0"
resolved "https://registry.yarnpkg.com/babel-core/-/babel-core-7.0.0-bridge.0.tgz#95a492ddd90f9b4e9a4a1da14eb335b87b634ece"
Expand Down Expand Up @@ -2560,7 +2576,7 @@ columnify@~1.5.4:
strip-ansi "^3.0.0"
wcwidth "^1.0.0"

combined-stream@^1.0.6, combined-stream@~1.0.6:
combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6:
version "1.0.8"
resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
Expand Down Expand Up @@ -2779,6 +2795,13 @@ dashdash@^1.12.0:
dependencies:
assert-plus "^1.0.0"

date-fns@^2.30.0:
version "2.30.0"
resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-2.30.0.tgz#f367e644839ff57894ec6ac480de40cae4b0f4d0"
integrity sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==
dependencies:
"@babel/runtime" "^7.21.0"

dateformat@^3.0.0:
version "3.0.3"
resolved "https://registry.yarnpkg.com/dateformat/-/dateformat-3.0.3.tgz#a6e37499a4d9a9cf85ef5872044d62901c9889ae"
Expand Down Expand Up @@ -3598,6 +3621,11 @@ flow-parser@^0.185.0:
resolved "https://registry.yarnpkg.com/flow-parser/-/flow-parser-0.185.2.tgz#cb7ee57f77377d6c5d69a469e980f6332a15e492"
integrity sha512-2hJ5ACYeJCzNtiVULov6pljKOLygy0zddoqSI1fFetM+XRPpRshFdGEijtqlamA1XwyZ+7rhryI6FQFzvtLWUQ==

follow-redirects@^1.15.0:
version "1.15.2"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==

for-each@^0.3.3:
version "0.3.3"
resolved "https://registry.yarnpkg.com/for-each/-/for-each-0.3.3.tgz#69b447e88a0a5d32c3e7084f3f1710034b21376e"
Expand All @@ -3615,6 +3643,15 @@ forever-agent@~0.6.1:
resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91"
integrity sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==

form-data@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
mime-types "^2.1.12"

form-data@~2.3.2:
version "2.3.3"
resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.3.tgz#dcce52c05f644f298c6a7ab936bd724ceffbf3a6"
Expand Down Expand Up @@ -6580,6 +6617,11 @@ prop-types@*, prop-types@^15.8.1:
object-assign "^4.1.1"
react-is "^16.13.1"

proxy-from-env@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==

psl@^1.1.28:
version "1.9.0"
resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7"
Expand Down

0 comments on commit 7bee58c

Please sign in to comment.