├── .coveralls.yml ├── .gitignore ├── .scrutinizer.yml ├── .travis.yml ├── LICENSE.txt ├── README.md ├── README.rst ├── license.sh ├── ravepaypysdk ├── __init__.py ├── api.py ├── api_exceptions.py ├── helpers.py ├── resources.py └── utils │ ├── __init__.py │ ├── encryption.py │ └── rave_utils.py ├── requirements.txt ├── setup.py └── tests ├── test_api.py ├── test_api_exceptions.py ├── test_helpers.py └── test_resources.py /.coveralls.yml: -------------------------------------------------------------------------------- 1 | repo_token: ${COVERALLS_REPO_TOKEN} 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | .env 3 | *.pyc 4 | .DS_Store 5 | .idea/ 6 | .coverage 7 | cover/ 8 | /*.egg-info 9 | /*.egg 10 | /*.eggs 11 | test-env/ 12 | build/ 13 | -------------------------------------------------------------------------------- /.scrutinizer.yml: -------------------------------------------------------------------------------- 1 | checks: 2 | python: 3 | code_rating: true 4 | duplicate_code: true 5 | filter: 6 | excluded_paths: 7 | - tests/* 8 | - '*/site-packages/*' 9 | 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "3.6" 4 | 5 | install: 6 | - pip install -r requirements.txt 7 | 8 | script: 9 | - nosetests --with-coverage --cover-erase --cover-package=ravepaypysdk --cover-html 10 | - coveralls 11 | 12 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | Copyright (c) 2018 Johnbosco Ohia 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | The above copyright notice and this permission notice shall be 11 | included in all copies or substantial portions of the Software. 12 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 13 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 14 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 15 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 16 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 17 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 18 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RavePay-SDK-Python 2 | [![Build Status](https://travis-ci.org/johnchuks/ravepay-python-sdk.svg?branch=master)](https://travis-ci.org/johnchuks/ravepay-python-sdk) 3 | [![Scrutinizer Code Quality](https://scrutinizer-ci.com/g/johnchuks/RavePay-SDK-Python/badges/quality-score.png?b=master)](https://scrutinizer-ci.com/g/johnchuks/RavePay-SDK-Python/?branch=master) 4 | [![Coverage Status](https://coveralls.io/repos/github/johnchuks/RavePay-SDK-Python/badge.svg?branch=master)](https://coveralls.io/github/johnchuks/RavePay-SDK-Python?branch=master) 5 | [![PyPI version](https://badge.fury.io/py/ravepaypysdk.svg)](https://badge.fury.io/py/ravepaypysdk) 6 | 7 | The RavePay Python SDK provides APIs to create, process and manage payments on the RavePay platform. The SDK fully supports the API 8 | 9 | ## Installation 10 | `pip install ravepaypysdk` 11 | 12 | ## Configuring RavePay SDK 13 | ``` 14 | import ravepaypysdk 15 | 16 | my_api = ravepaypysdk.Api( 17 | secret_key='ravepay_secret_key' 18 | public_key='ravepay_public_key' 19 | production=False # sandbox # or True # Live 20 | ) 21 | ``` 22 | 23 | ## Usage 24 | To start using the SDK ensure you have your public key and secret key instantiated with the `Api` object 25 | 26 | ## Payments (RavePay Direct Charge) 27 | Import the RavePay Payment module 28 | ``` 29 | from ravepaypysdk import Payment 30 | ``` 31 | There are 5 different ways to utilize RavePay's direct charge for payment: 32 | 33 | - **Card direct charge** 34 | 35 | *Usage* 36 | ``` 37 | payload = { 38 | "cardno": "5438898014560229", 39 | "cvv": "789", 40 | "expirymonth": "07", 41 | "expiryyear": "18", 42 | "currency": "NGN", 43 | "pin": "7552", 44 | "country": "GH", 45 | "amount": "10", 46 | "email": "user@example.com", 47 | "phonenumber": "1234555", 48 | "suggested_auth": "PIN", 49 | "firstname": "user1", 50 | "lastname": "user2", 51 | "IP": "355426087298442", 52 | "txRef": "MC-7663-YU", 53 | "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c" 54 | } 55 | card_payment = Payment.card(payload, api=my_api) 56 | 57 | if card_payment: 58 | return card_payment 59 | ``` 60 | 61 | - **Bank account payment** 62 | 63 | *Usage* 64 | ``` 65 | payload = { 66 | "accountnumber": "0690000004", 67 | "accountbank": "044", 68 | "currency": "NGN", 69 | "country": "NG", 70 | "amount": "10", 71 | "email": "user@example.com", 72 | "phonenumber": "1234555", 73 | "firstname": "first name", 74 | "lastname": "last name", 75 | "IP": "355426087298442", 76 | "txRef": "", 77 | "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c" 78 | } 79 | bank_payment = Payment.bank_account(payload, api=my_api) 80 | 81 | if bank_payment: 82 | return bank_payment 83 | 84 | ``` 85 | - **Tokenize Card** 86 | 87 | *Usage* 88 | ``` 89 | payload = { 90 | "token":{ 91 | “chargeToken”:{ 92 | “user_token”:“f0209”, 93 | “embed_token”:“flw-t0-9f3aa69a806f6440fbb78cc9e8b2f135-k3n” 94 | } 95 | }, 96 | "currency": "NGN", 97 | "country": "NG", 98 | "amount": "10", 99 | "email": "user@example.com", 100 | "phonenumber": "1234555", 101 | "firstname": "user1", 102 | "lastname": "user2", 103 | "IP": "355426087298442", 104 | "txRef": "MC-7663-YU", 105 | "narration":"tokenize my card" 106 | "meta": [{"metaname": "flightId"}, {"metavalue": "1002"}] 107 | "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c" 108 | } 109 | 110 | tokenize_card = Payment.tokenize_card(payload, api=my_api) 111 | ``` 112 | - **USSD Payment** 113 | 114 | *Usage* 115 | ``` 116 | ussd_payment = Payment.ussd(payload, api=my_api) 117 | ``` 118 | 119 | - **MPESA Payment** 120 | 121 | *Usage* 122 | ``` 123 | mpesa_payment = Payment.mpesa(payload, api=my_api) 124 | ``` 125 | 126 | - **Ghana Mobile Money** 127 | 128 | *Usage* 129 | ``` 130 | gh_mobile_payment = Payment.ghana_mobile(payload, api=my_api) 131 | ``` 132 | 133 | - The same payload format goes for the **USSD**, **MPESA** and **Ghana Mobile Money**. Kindly review the [API documentation](https://flutterwavedevelopers.readme.io/v1.0/reference#rave-parameters) to get the required field for each transaction 134 | 135 | ## Transaction 136 | This module retrieves all transactions and verifies transactions. 137 | 138 | *Usage* 139 | 140 | Import the transaction module 141 | 142 | from ravepaypysdk import Transaction 143 | 144 | 145 | - **Verify your transaction** 146 | 147 | *Usage* 148 | ``` 149 | payload = { 150 | "flw_ref": "FLW-MOCK-6f52518a2ecca2b6b090f9593eb390ce", # unique reference for the transaction 151 | "tx_ref":"dummy", # merchants unique reference number 152 | "normalize": "1" 153 | } 154 | verify_transaction = Transaction.verify(payload, api=my_api) 155 | 156 | ``` 157 | 158 | - **verify transaction with xquery** 159 | 160 | *Usage* 161 | ``` 162 | payload = { 163 | "flwref": "FLW-MOCK-6f52518a2ecca2b6b090f9593eb390ce", # unique reference for the transaction 164 | "txref":"dummy", # merchants unique reference number 165 | "last_attempt":"1", # retrieves the last transaction 166 | "only_successful": "1" # retrieves only successful transaction 167 | } 168 | xquery_verify = Transaction.verify_query(payload, api=my_api) 169 | 170 | ``` 171 | 172 | - **List all recurring transactions** 173 | 174 | *Usage* 175 | ``` 176 | list_transactions = Transaction.list_all_recurring(api=my_api) 177 | ``` 178 | 179 | 180 | - **List single recurring transactions** 181 | 182 | *Usage* 183 | ``` 184 | payload = { 185 | "txId":"dummy" #add the required value for txId 186 | } 187 | list_single_transaction = Transaction.list_single_recurring(payload, api=my_api) 188 | ``` 189 | 190 | ## PreAuthorization 191 | This module performs preauthorization transactions on the RavePay platform 192 | 193 | *Usage* 194 | 195 | Import the PreAuthorization module 196 | ``` 197 | from ravepaypysdk import PreAuthorization 198 | ``` 199 | - **Preauthorization Capture** 200 | 201 | *Usage* 202 | ``` 203 | payload = { 204 | "flwRef":"39448fhdhhfdhshshf" # add the required value 205 | } 206 | preauthorize_capture = PreAuthorization.capture(payload, api=my_api) 207 | ``` 208 | 209 | 210 | - **Preauthorize Card transaction** 211 | 212 | *Usage* 213 | ``` 214 | Payload is the same as direct charge card payment payload 215 | 216 | preauthorize_card = PreAuthorization.card(payload, api=my_api) 217 | 218 | ``` 219 | 220 | 221 | - **Preauthorize void or refund transactions** 222 | 223 | *Usage* 224 | ``` 225 | payload = { 226 | "flwRef":"dummy" # add the value from the capture response 227 | "action": "refund or void" # select what action i.e refund or void 228 | } 229 | preauth_void_refund = PreAuthorization.void_or_refund(payload, api=my_api) 230 | ``` 231 | 232 | 233 | ## Validate Ravepay charges 234 | This module validates RavePay payment transactions. 235 | 236 | *Usage* 237 | 238 | Import the ValidateCharge module 239 | 240 | ``` 241 | from ravepaypysdk import ValidateCharge 242 | payload = { 243 | "transaction_reference": "222334304", 244 | "otp": "12345" 245 | } 246 | ``` 247 | 248 | - **Validate card transactions** 249 | 250 | *Usage* 251 | ``` 252 | 253 | validate_card_transac = ValidateCharge.card(payload, api=my_api) 254 | 255 | ``` 256 | 257 | - **Validate bank account transaction** 258 | 259 | *Usage* 260 | ``` 261 | validate_bank_account_transac = ValidateCharge.account(payload, api=my_api) 262 | ``` 263 | 264 | ## Subscriptions 265 | This module retrieves, cancels and activates subscriptions on the RavePay Platform 266 | 267 | *Usage* 268 | 269 | Import the subscriptions module 270 | 271 | ``` 272 | from ravepaypysdk import Subscriptions 273 | 274 | ``` 275 | - **Activate Subscription** 276 | 277 | *Usage* 278 | ``` 279 | #sub_id is the subscription id 280 | activate_subscription = Subscriptions.activate(sub_id=50, api=my_api) 281 | ``` 282 | 283 | - **Cancel Subscription** 284 | 285 | *Usage* 286 | ``` 287 | #sub_id is the subscription id 288 | cancel_subscription = Subscriptions.cancel(sub_id=50, api=my_api) 289 | 290 | ``` 291 | - **Fetch Single Subscription** 292 | 293 | *Usage* 294 | ``` 295 | #params is the subscription id 296 | single_subscription = Subscriptions.fetch_single(params=70, api=my_api) 297 | ``` 298 | 299 | - **Fetch All Subscriptions** 300 | 301 | *Usage* 302 | ``` 303 | all_subscriptions = Subscriptions.fetch_all(api=my_api) 304 | ``` 305 | 306 | 307 | ## Payment Plan 308 | This module creates, retrieves, edit and cancel payment plans on the platform 309 | 310 | *Usage* 311 | 312 | Import the Payment plan module 313 | ``` 314 | from ravepaypysdk import PaymentPlan 315 | 316 | #plan_id is the payment plan identifier 317 | ``` 318 | 319 | - **Create Payment Plan** 320 | 321 | *Usage* 322 | ``` 323 | payload = { 324 | "amount": 3000, 325 | "name": 'New User', 326 | "intervals": "daily", 327 | "duration": 0 328 | } 329 | create_plan = PaymentPlan.create(payload, api=my_api) 330 | ``` 331 | 332 | - **Edit Payment Plan** 333 | 334 | *Usage* 335 | ``` 336 | payload = { 337 | "amount": 1000, 338 | "name": 'New User', 339 | "intervals": "monthly", 340 | "duration": 0 341 | } 342 | 343 | edit_plan = PaymentPlan.edit_plan(payload, plan_id=60, api=my_api) 344 | 345 | ``` 346 | 347 | - **Cancel Payment** 348 | 349 | *Usage* 350 | ``` 351 | cancel_plan = PaymentPlan.cancel_plan(plan_id=60, api=my_api) 352 | 353 | ``` 354 | 355 | - **Fetch Single Payment Plan** 356 | 357 | *Usage* 358 | ``` 359 | fetch_single_plan = PaymentPlan.fetch_single_plan(params=40, api=my_api) 360 | 361 | ``` 362 | 363 | - **Fetch All Payment Plans** 364 | 365 | *Usage* 366 | ``` 367 | fetch_all_plans = PaymentPlan.fetch_all_plan(api=my_api) 368 | ``` 369 | 370 | ## Miscellanous 371 | This module gets the bank list and current forex rates 372 | 373 | *Usage* 374 | 375 | Import the bank module 376 | 377 | ``` 378 | from ravepaypysdk import Bank 379 | ``` 380 | 381 | - **Get List of Banks** 382 | 383 | *Usage* 384 | ``` 385 | get_banks = Bank.list_all(api=my_api) 386 | ``` 387 | 388 | - **Get Forex Rates** 389 | 390 | *Usage* 391 | ``` 392 | payload = { 393 | 'origin_currency': 'USD', 394 | 'destination_currency': 'NGN', 395 | 'amount': '200' 396 | } 397 | get_forex = Bank.get_forex(payload, api=my_api) 398 | ``` 399 | 400 | ## Documentation 401 | API documentation for RavePay can be found [here](https://flutterwavedevelopers.readme.io/v1.0/reference#introduction) 402 | 403 | 404 | ## Contributing 405 | 406 | Contributions are always welcomed to the project. Use Github Issue for requests. 407 | 408 | - Fork the project to your repository then clone it to your local machine. 409 | - Create a new branch and make the necessary enhancement to the features. 410 | - If the you wish to update an existing enhancement submit a pull request. 411 | - If you are unsure about certain areas in the project feel to ask for assistance. 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | RavePay-SDK-Python 2 | ================== 3 | 4 | |Build Status| |Scrutinizer Code Quality| |Coverage Status| 5 | 6 | The RavePay Python SDK provides APIs to create, process and manage 7 | payments on the RavePay platform. The SDK fully supports the API 8 | 9 | Installation 10 | ------------ 11 | 12 | ``pip install ravepaypysdk`` 13 | 14 | Configuring RavePay SDK 15 | ----------------------- 16 | 17 | :: 18 | 19 | import ravepaypysdk 20 | 21 | my_api = ravepaypysdk.Api( 22 | secret_key='ravepay_secret_key' 23 | public_key='ravepay_public_key' 24 | production=False # sandbox # or True # Live 25 | ) 26 | 27 | Usage 28 | ----- 29 | 30 | To start using the SDK ensure you have your public key and secret key 31 | instantiated with the ``Api`` object 32 | 33 | Payments (RavePay Direct Charge) 34 | -------------------------------- 35 | 36 | Import the RavePay Payment module 37 | 38 | ``from ravepaypysdk import Payment`` 39 | 40 | There are 5 different ways to utilize RavePay’s direct charge for 41 | payment: 42 | 43 | - **Card direct charge** 44 | 45 | *Usage* 46 | 47 | :: 48 | 49 | payload = { 50 | “cardno”: “5438898014560229”, 51 | “cvv”: “789”, 52 | “expirymonth”: “07”, 53 | “expiryyear”: “18”, 54 | “currency”: “NGN”, 55 | “pin”:“7552”, 56 | “country”: “GH”, 57 | “amount”: “10”, 58 | “email”: “user@example.com”, 59 | “phonenumber”: “1234555”, 60 | “suggested_auth”: “PIN”, 61 | “firstname”:“user1”, 62 | “lastname”: “user2”, “IP”: “355426087298442”, “txRef”: 63 | “MC-7663-YU”, “device_fingerprint”: 64 | “69e6b7f0b72037aa8428b70fbe03986c” 65 | } 66 | card_payment = Payment.card(payload, api=my_api) 67 | 68 | if card_payment: 69 | return card_payment 70 | 71 | - **Bank account payment** 72 | 73 | *Usage* 74 | 75 | :: 76 | 77 | payload = { 78 | "accountnumber": "0690000004", 79 | "accountbank": "044", 80 | "currency": "NGN", 81 | "country": "NG", 82 | "amount": "10", 83 | "email": "user@example.com", 84 | "phonenumber": "1234555", 85 | "firstname": "first name", 86 | "lastname": "last name", 87 | "IP": "355426087298442", 88 | "txRef": "", 89 | "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c" 90 | } 91 | bank_payment = Payment.bank_account(payload, api=my_api) 92 | 93 | if bank_payment: 94 | return bank_payment 95 | 96 | - **Tokenize Card** 97 | 98 | *Usage* 99 | 100 | :: 101 | 102 | payload = { 103 | "token":{ 104 | “chargeToken”:{ 105 | “user_token”:“f0209”, 106 | “embed_token”:“flw-t0-9f3aa69a806f6440fbb78cc9e8b2f135-k3n” 107 | } 108 | }, 109 | "currency": "NGN", 110 | "country": "NG", 111 | "amount": "10", 112 | "email": "user@example.com", 113 | "phonenumber": "1234555", 114 | "firstname": "user1", 115 | "lastname": "user2", 116 | "IP": "355426087298442", 117 | "txRef": "MC-7663-YU", 118 | "narration":"tokenize my card" 119 | "meta": [{"metaname": "flightId"}, {"metavalue": "1002"}] 120 | "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c" 121 | } 122 | 123 | tokenize_card = Payment.tokenize_card(payload, api=my_api) 124 | 125 | - **USSD Payment** 126 | 127 | *Usage* 128 | 129 | :: 130 | 131 | ussd_payment = Payment.ussd(payload, api=my_api) 132 | 133 | - **MPESA Payment** 134 | 135 | *Usage* 136 | 137 | :: 138 | 139 | mpesa_payment = Payment.mpesa(payload, api=my_api) 140 | 141 | - **Ghana Mobile Money** 142 | 143 | *Usage* 144 | 145 | :: 146 | 147 | gh_mobile_payment = Payment.ghana_mobile(payload, api=my_api) 148 | 149 | - The same payload format goes for the **USSD**, **MPESA** and **Ghana 150 | Mobile Money**. Kindly review the `API 151 | documentation `__ 152 | to get the required field for each transaction 153 | 154 | Transaction 155 | ----------- 156 | 157 | This module retrieves all transactions and verifies transactions. 158 | 159 | *Usage* 160 | 161 | Import the transaction module 162 | 163 | :: 164 | 165 | from ravepaypysdk import Transaction 166 | 167 | - **Verify your transaction** 168 | 169 | *Usage* 170 | 171 | :: 172 | 173 | payload = { 174 | “flw_ref”:“FLW-MOCK-6f52518a2ecca2b6b090f9593eb390ce”, # unique reference 175 | “tx_ref”:“dummy”, # merchants unique reference number 176 | “normalize”: “1” 177 | } 178 | verify_transaction = Transaction.verify(payload, api=my_api) 179 | 180 | 181 | - **verify transaction with xquery** 182 | 183 | *Usage* 184 | 185 | :: 186 | 187 | payload = { 188 | “flwref”: “FLW-MOCK-6f52518a2ecca2b6b090f9593eb390ce”, # unique reference 189 | “txref”:“dummy”, # merchants unique reference number 190 | “last_attempt”:“1”, # retrieves the last transaction 191 | “only_successful”: “1” # retrieves only successful transaction 192 | } 193 | xquery_verify = Transaction.verify_query(payload, api=my_api) 194 | 195 | 196 | - **List all recurring transactions** 197 | 198 | *Usage* 199 | 200 | :: 201 | 202 | list_transactions = Transaction.list_all_recurring(api=my_api) 203 | 204 | - **List single recurring transactions** 205 | 206 | *Usage* 207 | 208 | :: 209 | 210 | payload = { 211 | "txId":"dummy" #add the required value for txId 212 | } 213 | list_single_transaction = Transaction.list_single_recurring(payload, api=my_api) 214 | 215 | PreAuthorization 216 | ---------------- 217 | 218 | This module performs preauthorization transactions on the RavePay 219 | platform 220 | 221 | *Usage* 222 | 223 | Import the PreAuthorization module 224 | 225 | ``from ravepaypysdk import PreAuthorization`` - 226 | 227 | **Preauthorization Capture** 228 | 229 | :: 230 | 231 | *Usage* 232 | :: 233 | payload = { 234 | "flwRef":"39448fhdhhfdhshshf" # add the required value 235 | } 236 | preauthorize_capture = PreAuthorization.capture(payload, api=my_api) 237 | 238 | 239 | 240 | - **Preauthorize Card transaction** 241 | 242 | *Usage* 243 | 244 | :: 245 | 246 | Payload is the same as direct charge card payment payload 247 | 248 | preauthorize_card = PreAuthorization.card(payload, api=my_api) 249 | 250 | - **Preauthorize void or refund transactions** 251 | 252 | *Usage* 253 | 254 | :: 255 | 256 | payload = { 257 | "flwRef":"dummy" # add the value from the capture response 258 | "action": "refund or void" # select what action i.e refund or void 259 | } 260 | preauth_void_refund = PreAuthorization.void_or_refund(payload, api=my_api) 261 | 262 | 263 | 264 | Validate Ravepay charges 265 | ------------------------ 266 | 267 | This module validates RavePay payment 268 | transactions. 269 | 270 | *Usage* 271 | 272 | Import the ValidateCharge module 273 | 274 | :: 275 | 276 | from ravepaypysdk import ValidateCharge 277 | payload = { 278 | "transaction_reference": "222334304", 279 | "otp": "12345" 280 | } 281 | 282 | - **Validate card transactions** 283 | 284 | *Usage* 285 | 286 | :: 287 | 288 | 289 | validate_card_transac = ValidateCharge.card(payload, api=my_api) 290 | 291 | - **Validate bank account transaction** 292 | 293 | *Usage* 294 | 295 | :: 296 | 297 | validate_bank_account_transac = ValidateCharge.account(payload, api=my_api) 298 | 299 | 300 | 301 | Subscriptions 302 | ------------- 303 | 304 | This module retrieves, cancels and activates subscriptions on the 305 | RavePay Platform 306 | 307 | *Usage* 308 | 309 | Import the subscriptions module 310 | 311 | :: 312 | 313 | from ravepaypysdk import Subscriptions 314 | 315 | - **Activate Subscription** 316 | 317 | *Usage* 318 | 319 | :: 320 | 321 | #sub_id is the subscription id 322 | activate_subscription = Subscriptions.activate(sub_id=50, api=my_api) 323 | 324 | - **Cancel Subscription** 325 | 326 | *Usage* 327 | 328 | :: 329 | 330 | #sub_id is the subscription id 331 | cancel_subscription = Subscriptions.cancel(sub_id=50, api=my_api) 332 | 333 | - **Fetch Single Subscription** 334 | 335 | *Usage* 336 | 337 | :: 338 | 339 | #params is the subscription id 340 | single_subscription = Subscriptions.fetch_single(params=70, api=my_api) 341 | 342 | - **Fetch All Subscriptions** 343 | 344 | *Usage* 345 | 346 | :: 347 | 348 | all_subscriptions = Subscriptions.fetch_all(api=my_api) 349 | 350 | 351 | 352 | Payment Plan 353 | ------------ 354 | 355 | This module creates, retrieves, edit and cancel payment plans on the 356 | platform 357 | 358 | *Usage* 359 | 360 | Import the Payment plan module 361 | 362 | :: 363 | 364 | from ravepaypysdk import PaymentPlan 365 | 366 | #plan_id is the payment plan identifier 367 | 368 | - **Create Payment Plan** 369 | 370 | *Usage* 371 | 372 | :: 373 | 374 | payload = { 375 | "amount": 3000, 376 | "name": 'New User', 377 | "intervals": "daily", 378 | "duration": 0 379 | } 380 | create_plan = PaymentPlan.create(payload, api=my_api) 381 | 382 | - **Edit Payment Plan** 383 | 384 | *Usage* 385 | 386 | :: 387 | 388 | payload = { 389 | "amount": 1000, 390 | "name": 'New User', 391 | "intervals": "monthly", 392 | "duration": 0 393 | } 394 | 395 | edit_plan = PaymentPlan.edit_plan(payload, plan_id=60, api=my_api) 396 | 397 | - **Cancel Payment** 398 | 399 | *Usage* 400 | 401 | :: 402 | 403 | cancel_plan = PaymentPlan.cancel_plan(plan_id=60, api=my_api) 404 | 405 | - **Fetch Single Payment Plan** 406 | 407 | *Usage* 408 | 409 | :: 410 | 411 | fetch_single_plan = PaymentPlan.fetch_single_plan(params=40, api=my_api) 412 | 413 | - **Fetch All Payment Plans** 414 | 415 | *Usage* 416 | 417 | :: 418 | 419 | fetch_all_plans = PaymentPlan.fetch_all_plan(api=my_api) 420 | 421 | 422 | Miscellanous 423 | ------------ 424 | 425 | This module gets the bank list and current forex rates 426 | 427 | *Usage* 428 | 429 | Import the bank module 430 | 431 | :: 432 | 433 | from ravepaypysdk import Bank 434 | 435 | - **Get List of Banks** 436 | 437 | *Usage* 438 | 439 | :: 440 | 441 | get_banks = Bank.list_all(api=my_api) 442 | 443 | - **Get Forex Rates** 444 | 445 | *Usage* 446 | 447 | :: 448 | 449 | payload = { 450 | 'origin_currency': 'USD', 451 | 'destination_currency': 'NGN', 452 | 'amount': '200' 453 | } 454 | get_forex = Bank.get_forex(payload, api=my_api) 455 | 456 | Documentation 457 | ------------- 458 | 459 | API documentation for RavePay can be found 460 | `here `__ 461 | 462 | Contributing 463 | ------------ 464 | 465 | Contributions are always welcomed to the project. Use Github Issue for 466 | requests. 467 | 468 | - Fork the project to your repository then clone it to your local 469 | machine. 470 | - Create a new branch and make the necessary enhancement to the 471 | features. 472 | - If the you wish to update an existing enhancement submit a pull 473 | request. 474 | - If you are unsure about certain areas in the project feel to ask for 475 | assistance. 476 | 477 | .. |Build Status| image:: https://travis-ci.org/johnchuks/ravepay-python-sdk.svg?branch=master 478 | :target: https://travis-ci.org/johnchuks/ravepay-python-sdk 479 | .. |Scrutinizer Code Quality| image:: https://scrutinizer-ci.com/g/johnchuks/RavePay-SDK-Python/badges/quality-score.png?b=master 480 | :target: https://scrutinizer-ci.com/g/johnchuks/RavePay-SDK-Python/?branch=master 481 | .. |Coverage Status| image:: https://coveralls.io/repos/github/johnchuks/RavePay-SDK-Python/badge.svg?branch=master 482 | :target: https://coveralls.io/github/johnchuks/RavePay-SDK-Python?branch=master 483 | 484 | -------------------------------------------------------------------------------- /license.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh -e 2 | # Usage: license 3 | # Prints an MIT license appropriate for totin' around. 4 | # 5 | # http://www.opensource.org/licenses/mit-license.php 6 | # http://gist.github.com/287970 7 | # 8 | # $ license > COPYING 9 | #!/bin/sh 10 | echo "The MIT License 11 | Copyright (c) `date +%Y` Johnbosco Ohia 12 | Permission is hereby granted, free of charge, to any person obtaining 13 | a copy of this software and associated documentation files (the 14 | \"Software\"), to deal in the Software without restriction, including 15 | without limitation the rights to use, copy, modify, merge, publish, 16 | distribute, sublicense, and/or sell copies of the Software, and to 17 | permit persons to whom the Software is furnished to do so, subject to 18 | the following conditions: 19 | The above copyright notice and this permission notice shall be 20 | included in all copies or substantial portions of the Software. 21 | THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, 22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 24 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 25 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 26 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 27 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." > LICENSE.txt 28 | -------------------------------------------------------------------------------- /ravepaypysdk/__init__.py: -------------------------------------------------------------------------------- 1 | # pylint: skip-file 2 | 3 | from .utils.encryption import encrypt_data, get_key 4 | from .utils.rave_utils import initialize_config, merge_dict, merge_url 5 | from .api import Api 6 | from .api_exceptions import ApiError 7 | from .resources import PreAuthorization, Payment, ValidateCharge, Bank, Transaction, PaymentPlan, Subscriptions 8 | -------------------------------------------------------------------------------- /ravepaypysdk/api.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains the default API 3 | object for the SDK. It contains the request handlers and 4 | response handler respectively 5 | """ 6 | 7 | import json 8 | 9 | import requests 10 | 11 | from ravepaypysdk.api_exceptions import ApiError 12 | from ravepaypysdk.utils.rave_utils import get_url, merge_url 13 | 14 | 15 | class Api(object): 16 | """ 17 | Default Object for RavePay Api 18 | """ 19 | secret_key = None 20 | public_key = None 21 | 22 | def __init__(self, **kwargs): 23 | self.secret_key = kwargs.get('secret_key') 24 | self.public_key = kwargs.get('public_key') 25 | self.mode = kwargs.get('production') 26 | self.title = '**RavePayPYSDK**' 27 | self.payload = None 28 | self.query_string = None 29 | 30 | if not self.mode: 31 | self.url = get_url(mode='sandbox') 32 | else: 33 | self.url = get_url(mode='live') 34 | 35 | def __repr__(self): 36 | return "{}".format(self.title) 37 | 38 | @staticmethod 39 | def handle_response(response, content): 40 | """Validate HTTP Response""" 41 | status = response.status_code 42 | 43 | if status == 200 or status == 201: 44 | response = dict(status_code=status, content=json.loads(content)) 45 | if content: 46 | return response 47 | elif status == 400: 48 | api_error = ApiError(response, content) 49 | return api_error 50 | 51 | def request(self, method, url, **kwargs): 52 | """handles request to RavePay API""" 53 | http_header = dict(content_type='application/json') 54 | 55 | self.payload = kwargs.get('payload') 56 | self.query_string = kwargs.get('params') 57 | if self.payload is not None and self.query_string is None: 58 | response = requests.request( 59 | method, url, data=self.payload, headers=http_header) 60 | return Api.handle_response(response, response.content.decode('utf-8')) 61 | 62 | if self.payload is None and self.query_string is None: 63 | response = requests.request(method, url, headers=http_header) 64 | return Api.handle_response(response, response.content.decode('utf-8')) 65 | 66 | if self.query_string is not None and self.payload is None: 67 | response = requests.request( 68 | method, url, headers=http_header, params=self.query_string) 69 | return Api.handle_response(response, response.content.decode('utf-8')) 70 | 71 | def get(self, endpoint, query_string=None): 72 | """ 73 | Make a GET Request 74 | """ 75 | 76 | if query_string is not None: 77 | return self.request( 78 | 'GET', merge_url(self.url, endpoint), 79 | payload=None, params=query_string) 80 | return self.request( 81 | 'GET', merge_url(self.url, endpoint), 82 | payload=None, params=None 83 | ) 84 | 85 | def post(self, endpoint, payload): 86 | """ 87 | Make a POST Request 88 | """ 89 | return self.request( 90 | 'POST', 91 | merge_url(self.url, endpoint), payload=payload 92 | ) 93 | 94 | def put(self, endpoint, payload, query_string=None): 95 | """ 96 | Make a PUT Request 97 | """ 98 | return self.request( 99 | 'PUT', 100 | merge_url(self.url, endpoint), 101 | payload=payload, params=query_string 102 | ) 103 | -------------------------------------------------------------------------------- /ravepaypysdk/api_exceptions.py: -------------------------------------------------------------------------------- 1 | """ Exception module for returning error message 2 | from RavePay""" 3 | 4 | class ApiError(object): 5 | """Error Class""" 6 | 7 | def __init__(self, response, content=None): 8 | """initialize attributes for the class""" 9 | self.response = response 10 | self.content = content 11 | 12 | def __str__(self): 13 | """convert object to string""" 14 | error_message = 'Error. ' 15 | if hasattr(self.response, 'status_code'): 16 | error_message += "Response status code: {}".format(self.response.status_code) 17 | if self.content is not None: 18 | error_message += " Response content: {}".format(self.content) 19 | return error_message 20 | -------------------------------------------------------------------------------- /ravepaypysdk/helpers.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains the helper class 3 | for REST services 4 | """ 5 | 6 | 7 | class Create(object): 8 | """ 9 | Utility for class for POST rest service 10 | """ 11 | 12 | @classmethod 13 | def create(cls, endpoint, api, payload=None): 14 | """ 15 | Process POST REST operations 16 | """ 17 | if payload is not None: 18 | return api.post(endpoint, payload) 19 | 20 | 21 | class List(object): 22 | """ 23 | Utility for class for GET rest service 24 | """ 25 | 26 | @classmethod 27 | def list(cls, endpoint, api, params=None): 28 | """ 29 | Process GET rest operations 30 | """ 31 | if params is not None: 32 | return api.get(endpoint, params) 33 | elif params is None: 34 | return api.get(endpoint) 35 | -------------------------------------------------------------------------------- /ravepaypysdk/resources.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module handles all the REST Services for RavePay 3 | """ 4 | 5 | from .helpers import Create, List 6 | from .utils.rave_utils import merge_dict, initialize_config 7 | 8 | 9 | class PreAuthorization(Create): 10 | """ 11 | Preauthorization for refunds and card transactions 12 | """ 13 | 14 | @classmethod 15 | def capture(cls, payload, api): 16 | """ 17 | process preauthorization of capture transactions 18 | 19 | """ 20 | endpoint = '/flwv3-pug/getpaidx/api/capture' 21 | secret_key_dict = dict(SECKEY=api.secret_key) 22 | capture_payload = merge_dict(payload, secret_key_dict) 23 | if capture_payload: 24 | return cls.create(endpoint, api, capture_payload) 25 | 26 | @classmethod 27 | def preauthorize_card(cls, payload, api): 28 | """ 29 | process preauthorization of cards before transactions 30 | """ 31 | endpoint = '/flwv3-pug/getpaidx/api/charge' 32 | preauthorize_payload = initialize_config(payload, api) 33 | if preauthorize_payload: 34 | return cls.create(endpoint, api, preauthorize_payload) 35 | 36 | @classmethod 37 | def void_or_refund(cls, payload, api): 38 | """ 39 | processes preauthorization of refunds and void transactions 40 | """ 41 | endpoint = '/flwv3-pug/getpaidx/api/refundorvoid' 42 | secret_key_dict = dict(SECKEY=api.secret_key) 43 | updated_void_payload = merge_dict(secret_key_dict, payload) 44 | return cls.create(endpoint, api, updated_void_payload) 45 | 46 | 47 | class ValidateCharge(Create): 48 | """ 49 | Validates Payment transactions for RavePay 50 | """ 51 | 52 | @classmethod 53 | def card(cls, payload, api): 54 | """ 55 | Validate direct charge payments made with card 56 | """ 57 | endpoint = '/flwv3-pug/getpaidx/api/validatecharge' 58 | public_key_dict = dict(PBFPubKey=api.public_key) 59 | revised_payload = merge_dict(payload, public_key_dict) 60 | request = cls.create(endpoint, api, revised_payload) 61 | return request 62 | 63 | @classmethod 64 | def account(cls, payload, api): 65 | """ 66 | Validate direct charge payments made with bank account 67 | """ 68 | account_endpoint = '/flwv3-pug/getpaidx/api/validate' 69 | public_key_dict = dict(PBFPubKey=api.public_key) 70 | revised_payload = merge_dict(payload, public_key_dict) 71 | return cls.create(account_endpoint, api, revised_payload) 72 | 73 | 74 | class Transaction(Create, List): 75 | """ 76 | Process Transactions with RavePay API 77 | """ 78 | 79 | @classmethod 80 | def verify(cls, payload, api): 81 | """ 82 | Verifies a user's transaction 83 | 84 | """ 85 | endpoint = '/flwv3-pug/getpaidx/api/verify' 86 | secret_key_dict = dict(SECKEY=api.secret_key) 87 | updated_valid_payload = merge_dict(payload, secret_key_dict) 88 | return cls.create(endpoint, api, updated_valid_payload) 89 | 90 | @classmethod 91 | def verify_query(cls, payload, api): 92 | """ 93 | Verifies a user's transaction with xrequery 94 | """ 95 | endpoint = '/flwv3-pug/getpaidx/api/xrequery' 96 | secret_key_dict = dict(SECKEY=api.secret_key) 97 | updated_valid_payload = merge_dict(payload, secret_key_dict) 98 | return cls.create(endpoint, api, updated_valid_payload) 99 | 100 | @classmethod 101 | def list_all_recurring(cls, api): 102 | """ 103 | Gets all recurring transactions 104 | """ 105 | endpoint = '/merchant/subscriptions/list' 106 | params = dict(seckey=api.secret_key) 107 | return cls.list(endpoint, api, params) 108 | 109 | @classmethod 110 | def list_single_recurring(cls, payload, api): 111 | """ 112 | Gets a single recurring transaction 113 | """ 114 | tx_id = payload.get('txId') 115 | endpoint = '/merchant/subscriptions/list' 116 | params = dict(seckey=api.secret_key, txId=tx_id) 117 | return cls.list(endpoint, api, params) 118 | 119 | @classmethod 120 | def refund(cls, payload, api): 121 | """ 122 | Process getting refunds after transaction 123 | """ 124 | endpoint = '/gpx/merchant/transactions/refund' 125 | secret_key_dict = dict(SECKEY=api.secret_key) 126 | updated_valid_payload = merge_dict(payload, secret_key_dict) 127 | return cls.create(endpoint, api, updated_valid_payload) 128 | 129 | @classmethod 130 | def stop_recurring_payment(cls, payload, api): 131 | """ 132 | processes stop recurring payment transacyions 133 | """ 134 | endpoint = "/merchant/subscriptions/stop" 135 | secret_key_dict = dict(seckey=api.secret_key) 136 | merchant_id = payload.get('id') 137 | if not merchant_id: 138 | raise KeyError('Id field is required for this transaction') 139 | else: 140 | updated_payload = merge_dict(secret_key_dict, payload) 141 | return cls.create(endpoint, api, updated_payload) 142 | 143 | 144 | class Bank(Create, List): 145 | """ 146 | Class for processing bank related information 147 | """ 148 | 149 | @classmethod 150 | def list_all(cls, api): 151 | """ 152 | Get all banks in Nigeria compatible with rave pay 153 | """ 154 | endpoint = '/flwv3-pug/getpaidx/api/flwpbf-banks.js?json=1' 155 | return cls.list(endpoint, api) 156 | 157 | @classmethod 158 | def get_forex(cls, payload, api): 159 | """ 160 | Get current forex exchange rates 161 | """ 162 | secret_key_dict = dict(SECKEY=api.secret_key) 163 | updated_valid_payload = merge_dict(payload, secret_key_dict) 164 | endpoint = '/flwv3-pug/getpaidx/api/forex' 165 | return cls.create(endpoint, api, updated_valid_payload) 166 | 167 | 168 | class Payment(Create): 169 | """ 170 | Class for processing rave direct charge payments 171 | """ 172 | endpoint = '/flwv3-pug/getpaidx/api/charge' 173 | 174 | @classmethod 175 | def card(cls, payload, api): 176 | """ 177 | Process direct charge payment from cards 178 | """ 179 | if not (payload.get('cardno') and payload.get('cvv')): 180 | raise KeyError('Card number and CVV are \ 181 | required for card transactions. Kindly check your payload') 182 | else: 183 | card_payload = initialize_config(payload, api) 184 | return cls.create(cls.endpoint, api, card_payload) 185 | 186 | @classmethod 187 | def bank_account(cls, payload, api): 188 | """ 189 | Process payment from bank accounts (Only available in Nigeria) 190 | """ 191 | account_number = payload.get('accountnumber') 192 | account_bank = payload.get('accountbank') 193 | if not (account_bank and account_number): 194 | raise KeyError( 195 | 'Account number and bank are required fields \ 196 | for this transaction.Kindly check the documentation') 197 | else: 198 | account_payload = initialize_config(payload, api) 199 | return cls.create(cls.endpoint, api, account_payload) 200 | 201 | @classmethod 202 | def mpesa(cls, payload, api): 203 | """ 204 | Processes mpesa money payment 205 | """ 206 | is_mpesa = payload.get('is_mpesa') 207 | payment_type = payload.get('payment-type') 208 | if not (is_mpesa and payment_type == 'mpesa'): 209 | raise KeyError('Mpesa money fields are required for this transaction') 210 | else: 211 | mpesa_payload = initialize_config(payload, api) 212 | return cls.create(cls.endpoint, api, mpesa_payload) 213 | 214 | @classmethod 215 | def ghana_mobile(cls, payload, api): 216 | """ 217 | Processes Ghana mobile money payment 218 | """ 219 | gh_mobile = payload.get('is_mobile_money_gh') 220 | payment_type = payload.get('payment-type') 221 | if not (gh_mobile and payment_type == 'mobilemoneygh'): 222 | raise KeyError('Ghana mobile money fields are required \ 223 | for this transaction') 224 | else: 225 | gh_mobile_money_payload = initialize_config(payload, api) 226 | return cls.create(cls.endpoint, api, gh_mobile_money_payload) 227 | 228 | @classmethod 229 | def ussd(cls, payload, api): 230 | """ 231 | Process USSD payment 232 | """ 233 | is_ussd = payload.get('is_ussd') 234 | payment_type = payload.get('payment_type') 235 | if not (is_ussd and payment_type == 'ussd'): 236 | raise KeyError('USSD fields are required for this transaction.\ 237 | Check your payload') 238 | else: 239 | ussd_payload = initialize_config(payload, api) 240 | return cls.create(cls.endpoint, api, ussd_payload) 241 | 242 | @classmethod 243 | def tokenize_card(cls, payload, api): 244 | """ 245 | This function allows users let their card be charged with tokens 246 | """ 247 | endpoint = 'flwv3-pug/getpaidx/api/tokenized/charge' 248 | secret_key_dict = dict(SECKEY=api.secret_key) 249 | email = payload.get('email') 250 | token_payload = merge_dict(secret_key_dict, payload) 251 | if not email: 252 | raise KeyError('You need to pass the same email \ 253 | used in the initial charge') 254 | return cls.create(endpoint, api, token_payload) 255 | 256 | 257 | class PaymentPlan(Create, List): 258 | """ 259 | Class for processing payment plan on the ravepay platform 260 | """ 261 | 262 | @classmethod 263 | def create_plan(cls, payload, api): 264 | endpoint = 'v2/gpx/paymentplans/create' 265 | secret_key_dict = dict(seckey=api.secret_key) 266 | plan_payload = merge_dict(payload, secret_key_dict) 267 | return cls.create(endpoint, api, plan_payload) 268 | 269 | @classmethod 270 | def fetch_all_plan(cls, api): 271 | endpoint = 'v2/gpx/paymentplans/query' 272 | secret_key_dict = dict(seckey=api.secret_key) 273 | return cls.list(endpoint, api, secret_key_dict) 274 | 275 | @classmethod 276 | def fetch_single_plan(cls, params, api): 277 | endpoint = 'v2/gpx/paymentplans/query' 278 | secret_key_dict = dict(seckey=api.secret_key) 279 | fetch_params = merge_dict(params, secret_key_dict) 280 | return cls.list(endpoint, api, fetch_params) 281 | 282 | @classmethod 283 | def cancel_plan(cls, plan_id=None, api=None): 284 | if plan_id is not None: 285 | endpoint = 'v2/gpx/paymentplans/{}/cancel'.format(plan_id) 286 | secret_key_dict = dict(seckey=api.secret_key) 287 | return cls.create(endpoint, api, secret_key_dict) 288 | return None 289 | 290 | @classmethod 291 | def edit_plan(cls, payload=None, plan_id=None, api=None): 292 | if plan_id is not None: 293 | endpoint = 'v2/gpx/paymentplans/{}/edit'.format(plan_id) 294 | secret_key_dict = dict(seckey=api.secret_key) 295 | if payload is not None: 296 | edit_payload = merge_dict(payload, secret_key_dict) 297 | return cls.create(endpoint, api, edit_payload) 298 | return cls.create(endpoint, api, secret_key_dict) 299 | 300 | 301 | class Subscriptions(Create, List): 302 | @classmethod 303 | def fetch_all(cls, api): 304 | endpoint = 'v2/gpx/subscriptions/query' 305 | secret_key_dict = dict(seckey=api.secret_key) 306 | return cls.list(endpoint, api, secret_key_dict) 307 | 308 | @classmethod 309 | def fetch_single(cls, params=None, api=None): 310 | endpoint = 'v2/gpx/subscriptions/query' 311 | if params is not None: 312 | secret_key = dict(seckey=api.secret_key) 313 | fetch_params_dict = merge_dict(params, secret_key) 314 | return cls.list(endpoint, api, fetch_params_dict) 315 | return None 316 | 317 | @classmethod 318 | def cancel(cls, sub_id=None, api=None): 319 | if sub_id is not None: 320 | endpoint = 'v2/gpx/subscriptions/{}/cancel'.format(sub_id) 321 | secret_key = dict(seckey=api.secret_key) 322 | return cls.create(endpoint, api, secret_key) 323 | return None 324 | 325 | @classmethod 326 | def activate(cls, sub_id=None, api=None): 327 | if sub_id is not None: 328 | endpoint = 'v2/gpx/subscriptions/{}/activate'.format(sub_id) 329 | secret_key = dict(seckey=api.secret_key) 330 | return cls.create(endpoint, api, secret_key) 331 | return None 332 | -------------------------------------------------------------------------------- /ravepaypysdk/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/johnchuks/ravepay-python-sdk/329d082280a982bfabee7d2a9ca6e9cfffaa3fc8/ravepaypysdk/utils/__init__.py -------------------------------------------------------------------------------- /ravepaypysdk/utils/encryption.py: -------------------------------------------------------------------------------- 1 | """ Module for encrypting payload sent for payment authorization """ 2 | 3 | import base64 4 | import hashlib 5 | 6 | from Crypto.Cipher import DES3 7 | 8 | 9 | def get_key(secret_key): 10 | """this is the getKey function that generates an encryption Key for you 11 | by passing your Secret Key as a parameter. 12 | """ 13 | hashed_secret_key = hashlib.md5(secret_key.encode("utf-8")).hexdigest() 14 | hashed_secret_key_last_12 = hashed_secret_key[-12:] 15 | secret_key_adjusted = secret_key.replace('FLWSECK-', '') 16 | secret_key_adjusted_first12 = secret_key_adjusted[:12] 17 | return secret_key_adjusted_first12 + hashed_secret_key_last_12 18 | 19 | 20 | def encrypt_data(secret_key, plain_text): 21 | """ 22 | This is the encryption function that encrypts your 23 | payload by passing the text and your encryption Key. 24 | """ 25 | encrypted_key = get_key(secret_key) 26 | block_size = 8 27 | to_add = len(plain_text) % block_size 28 | pad_diff = block_size - to_add 29 | cipher = DES3.new(encrypted_key, DES3.MODE_ECB) 30 | plain_text = "{}{}".format(plain_text, "".join(chr(pad_diff) * pad_diff)) 31 | encrypted = base64.b64encode(cipher.encrypt(plain_text)) 32 | return encrypted 33 | -------------------------------------------------------------------------------- /ravepaypysdk/utils/rave_utils.py: -------------------------------------------------------------------------------- 1 | """ Module for various utility or service functions""" 2 | 3 | import json 4 | import re 5 | from .encryption import encrypt_data 6 | 7 | 8 | SANDBOX = 'https://ravesandboxapi.flutterwave.com' 9 | LIVE = 'https://api.ravepay.co' 10 | 11 | 12 | def get_url(mode): 13 | """ Function for getting URL based on mode""" 14 | if mode == 'sandbox': 15 | return SANDBOX 16 | 17 | elif mode == 'live': 18 | return LIVE 19 | 20 | 21 | def merge_url(url, *url_paths): 22 | """ 23 | Utility function for merging url paths 24 | """ 25 | for path in url_paths: 26 | url = re.sub(r'/?$', re.sub(r'^/?', '/', path), url) 27 | return url 28 | 29 | 30 | def merge_dict(data, *override): 31 | """ 32 | Utility function for merging dictionaries 33 | """ 34 | result = {} 35 | for current_dict in (data,) + override: 36 | result.update(current_dict) 37 | return result 38 | 39 | 40 | def initialize_config(payload, api): 41 | """ 42 | Function for initializing payload for Rave direct charge 43 | """ 44 | stringify_payload = json.dumps(payload) 45 | public_key_algo_type_dict = dict(PBFPubKey=api.public_key, alg='3DES-24') 46 | encrypt_payload = encrypt_data(api.secret_key, stringify_payload) 47 | encrypt_payload_dict = dict(client=encrypt_payload) 48 | new_payload = merge_dict(encrypt_payload_dict, public_key_algo_type_dict) 49 | return new_payload 50 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2019.11.28 2 | chardet==3.0.4 3 | coverage==5.0.3 4 | coveralls==1.11.1 5 | idna==2.9 6 | nose==1.3.7 7 | pycrypto==2.6.1 8 | python-dotenv==0.12.0 9 | requests>=2.18.4 10 | urllib3==1.25.8 11 | 12 | 13 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup, find_packages 2 | from codecs import open 3 | 4 | setup(name='ravepaypysdk', 5 | version='1.0.2', 6 | description='A Python library to consume the RavePay API', 7 | long_description=open('README.rst', 'r').read(), 8 | url='https://github.com/johnchuks/ravepay-python-sdk', 9 | author='Johnbosco Ohia', 10 | author_email='johnboscoohia@gmail.com', 11 | license='MIT', 12 | keywords='ravepay python library', 13 | classifiers=[ 14 | 'Development Status :: 4 - Beta', 15 | 'Intended Audience :: Developers', 16 | 'License :: OSI Approved :: MIT License', 17 | 'Programming Language :: Python :: 2', 18 | 'Programming Language :: Python :: 2.6', 19 | 'Programming Language :: Python :: 2.7', 20 | 'Programming Language :: Python :: 3', 21 | 'Programming Language :: Python :: 3.3', 22 | 'Programming Language :: Python :: 3.4', 23 | 'Programming Language :: Python :: 3.5', 24 | 'Programming Language :: Python :: 3.6' 25 | 26 | ], 27 | packages=find_packages(exclude=['contrib', 'tests']), 28 | install_requires=[ 29 | 'requests', 'pycrypto' 30 | ], 31 | test_suite='nose.collector', 32 | test_requires=['nose'], 33 | extras_require={ 34 | 'test': ['coverage'] 35 | }, 36 | zip_safe=False) 37 | -------------------------------------------------------------------------------- /tests/test_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | import json 4 | from collections import namedtuple 5 | from unittest.mock import Mock, patch 6 | 7 | from dotenv import find_dotenv, load_dotenv 8 | 9 | from ravepaypysdk.api import Api 10 | from ravepaypysdk.api_exceptions import ApiError 11 | 12 | load_dotenv(find_dotenv()) 13 | 14 | 15 | class ApiTest(unittest.TestCase): 16 | def setUp(self): 17 | self.api = Api( 18 | secret_key=os.environ.get('secret_key'), 19 | public_key=os.environ.get('public_key'), 20 | production=False 21 | ) 22 | self.new_api = Api() 23 | self.account_attributes = { 24 | "accountnumber": "0690000031", 25 | "accountbank": "044", 26 | "currency": "NGN", 27 | "country": "NG", 28 | "amount": "10", 29 | "email": "johnb@gmail.com", 30 | "phonenumber": "07088691390", 31 | "firstname": "johnb", 32 | "lastname": "chuks", 33 | "IP": "355426087298442", 34 | "txRef": "", 35 | "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c" 36 | } 37 | 38 | self.gh_money_payload = { 39 | "cardno": "5438898014560229", 40 | "cvv": "789", 41 | "is_mobile_money_gh": "1", 42 | "payment-type": "mobilemoneygh", 43 | "expirymonth": "07", 44 | "expiryyear": "18", 45 | "currency": "NGN", 46 | "pin": "7552", 47 | "country": "GH", 48 | "amount": "10", 49 | "email": "user@example.com", 50 | "phonenumber": "1234555", 51 | "suggested_auth": "PIN", 52 | "firstname": "user1", 53 | "lastname": "user2", 54 | "IP": "355426087298442", 55 | "txRef": "MC-7663-YU", 56 | "device_fingerprint": "69e6b7f0b72037aa8428b70fbe03986c" 57 | } 58 | 59 | self.api.request = Mock() 60 | 61 | 62 | def test_ravepay_config(self): 63 | self.api_dev = Api( 64 | public_key='dummy', 65 | secret_key='dummy', 66 | production=False 67 | ) 68 | self.api_live = Api( 69 | public_key='dummy', 70 | secret_key='dummy', 71 | production=True 72 | ) 73 | self.assertEqual(self.api_dev.url, 'https://ravesandboxapi.flutterwave.com') 74 | self.assertEqual(self.api_live.url, 'https://api.ravepay.co') 75 | self.assertEqual(self.api_dev.secret_key, 'dummy') 76 | self.assertEqual(self.api_dev.public_key, 'dummy') 77 | 78 | def test_get(self): 79 | params_test = dict(SECKEY=os.environ.get('secret_key')) 80 | endpoint = '/merchant/subscriptions/list' 81 | 82 | self.api.get(endpoint, params_test) 83 | self.api.request.assert_called_once_with('GET', 84 | 'https://ravesandboxapi.flutterwave.com/merchant/subscriptions/list', 85 | params=params_test, payload=None 86 | ) 87 | def test_get_without_params(self): 88 | endpoint = '/merchant/subscriptions/list' 89 | self.api.get(endpoint) 90 | self.api.request.assert_called_once_with('GET', 'https://ravesandboxapi.flutterwave.com/merchant/subscriptions/list', 91 | params=None, payload=None) 92 | 93 | def test_post(self): 94 | endpoint = '/flwv3-pug/getpaidx/api/verify' 95 | payload = {'name': 'johnbosco', 'occupation': 'we there', 'SECKEY': os.environ.get('SECKEY')} 96 | 97 | self.api.post(endpoint, payload) 98 | self.api.request.assert_called_once_with( 99 | 'POST', 'https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/verify', payload=payload 100 | ) 101 | 102 | def test_put(self): 103 | endpoint = '/flwv3-pug/getpaidx/api/verify' 104 | params = dict(SECKEY=os.environ.get('secret_key')) 105 | self.api.put(endpoint, payload=self.account_attributes, query_string=params) 106 | 107 | self.api.request.assert_called_once_with( 108 | 'PUT', 'https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/verify', params=params, 109 | payload=self.account_attributes, 110 | ) 111 | 112 | def test_bad_request(self): 113 | self.api.request.return_value = {"status": "error", "message": "USSD charges can only be done in Ghana Cedis", 114 | "data": {"code": "ERR", 115 | "message": "USSD charges can only be done in Ghana Cedis"}} 116 | gh_money_charge = self.api.post('/flwv3-pug/getpaidx/api/charge', self.gh_money_payload) 117 | self.api.request.assert_called_once_with( 118 | 'POST', "https://ravesandboxapi.flutterwave.com/flwv3-pug/getpaidx/api/charge", 119 | payload=self.gh_money_payload 120 | ) 121 | self.assertEqual(gh_money_charge.get('status'), 'error') 122 | 123 | @patch('ravepaypysdk.api.requests.get') 124 | def test_request_get(self, mock): 125 | mock.return_value.status_code = 200 126 | mock.return_value.text = dict(error=False,cardno='34343', cvv='232', email='jb@gmail.com') 127 | path = 'http://jsonplaceholder.typicode.com/posts' 128 | params = dict(userId=1) 129 | response = self.new_api.request('GET', path, params=params) 130 | self.assertEqual(response['status_code'], 200) 131 | 132 | @patch('ravepaypysdk.api.requests.post') 133 | def test_request_post(self, mock): 134 | mock.return_value.status_code = 201 135 | payload = json.dumps(dict(title='rave api', body='sdk for rave', userId=1)) 136 | path = 'http://jsonplaceholder.typicode.com/posts' 137 | 138 | response = self.new_api.request('POST', path, payload=payload) 139 | 140 | self.assertEqual(response['status_code'], 201) 141 | 142 | @patch('ravepaypysdk.api.requests.get') 143 | def test_request_get_without_params(self, mock): 144 | mock.return_value.status_code = 200 145 | path = 'http://jsonplaceholder.typicode.com/posts' 146 | 147 | response = self.new_api.request('GET', path) 148 | 149 | self.assertEqual(response['status_code'], 200) 150 | 151 | @patch('ravepaypysdk.api_exceptions.ApiError') 152 | def test_handle_error(self, mock): 153 | self.Response = namedtuple('Response', 'status_code') 154 | response = self.Response(status_code=400) 155 | content = json.dumps(dict(title='rave api', body='sdk for rave', userId=1)) 156 | status_code = 400 157 | Api.handle_response(response, content) 158 | self.assertEqual(response.status_code, 400) 159 | 160 | def test_repr(self): 161 | self.assertEqual(repr(self.api), '**RavePayPYSDK**') 162 | 163 | 164 | 165 | 166 | 167 | 168 | -------------------------------------------------------------------------------- /tests/test_api_exceptions.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from collections import namedtuple 3 | from ravepaypysdk.api_exceptions import ApiError 4 | 5 | 6 | class TestApiExceptions(unittest.TestCase): 7 | def setUp(self): 8 | self.Response = namedtuple('Response', 'status_code') 9 | 10 | def test_error_without_content(self): 11 | response = self.Response(status_code="404") 12 | print(self.Response) 13 | error = ApiError(response, content=None) 14 | self.assertEqual(str(error), "Error. Response status code: %s" % response.status_code) 15 | 16 | def test_error_with_content(self): 17 | response = self.Response(status_code="400") 18 | content = dict(error='You have a bug somewhere') 19 | error = ApiError(response, content=content) 20 | self.assertEqual(str(error), "Error. Response status code: {} Response content: {}" .format(response.status_code, content)) 21 | 22 | def test_Error_without_response_or_content(self): 23 | error = ApiError({}) 24 | self.assertEqual(str(error), "Error. ") 25 | -------------------------------------------------------------------------------- /tests/test_helpers.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import patch 3 | from ravepaypysdk.helpers import Create, List 4 | from ravepaypysdk.api import Api 5 | 6 | 7 | class TestCreate(unittest.TestCase): 8 | def setUp(self): 9 | self.api = Api( 10 | secret_key='dummy', 11 | public_key='dummy', 12 | production=False 13 | ) 14 | self.endpoint = '/' 15 | self.payload = dict(name='Ravepay') 16 | 17 | @patch('ravepaypysdk.api.Api.post') 18 | def test_create_method(self, mock): 19 | self.create = Create.create(self.endpoint, self.api, self.payload) 20 | mock.assert_called_once_with(self.endpoint, self.payload) 21 | 22 | 23 | class TestList(unittest.TestCase): 24 | def setUp(self): 25 | self.api = Api( 26 | secret_key='dummy', 27 | public_key='dummy', 28 | production=False 29 | ) 30 | self.endpoint = '/' 31 | self.payload = dict(name='Ravepay') 32 | 33 | @patch('ravepaypysdk.api.Api.get') 34 | def test_list_with_params(self, mock): 35 | params=dict(id='2') 36 | self.list_with_params = List.list(self.endpoint, self.api, params=params) 37 | mock.assert_called_once_with(self.endpoint, params) 38 | 39 | @patch('ravepaypysdk.api.Api.get') 40 | def test_list_without_params(self, mock): 41 | self.list_with_params = List.list(self.endpoint, self.api, params=None) 42 | mock.assert_called_once_with(self.endpoint) 43 | -------------------------------------------------------------------------------- /tests/test_resources.py: -------------------------------------------------------------------------------- 1 | import os 2 | import unittest 3 | from unittest.mock import patch 4 | from dotenv import load_dotenv, find_dotenv 5 | from ravepaypysdk.api import Api 6 | from ravepaypysdk.resources import Transaction, Payment, Bank, PreAuthorization, ValidateCharge, PaymentPlan, Subscriptions 7 | 8 | load_dotenv(find_dotenv()) 9 | 10 | 11 | class TestTransaction(unittest.TestCase): 12 | def setUp(self): 13 | self.new_api = Api( 14 | secret_key='dummy', 15 | public_key='dummy', 16 | production=False 17 | ) 18 | self.payload = dict(error=False, Payment=1200, id=2) 19 | 20 | @patch('ravepaypysdk.resources.List.list') 21 | def test_single_recurring_transaction(self, mock): 22 | self.single_recurring_transac_payload = { 23 | "txId": 1 24 | } 25 | mc = mock.return_value 26 | mc.list.return_value = True 27 | result = Transaction.list_single_recurring(self.single_recurring_transac_payload, api=self.new_api) 28 | mock.assert_called_once_with('/merchant/subscriptions/list', self.new_api, {'seckey': 'dummy', 'txId': 1}) 29 | self.assertTrue(result) 30 | 31 | @patch('ravepaypysdk.resources.List.list') 32 | def test_list_all_recurring_transaction(self, mock): 33 | mc = mock.return_value 34 | mc.list.return_value = True 35 | response = Transaction.list_all_recurring(api=self.new_api) 36 | mock.assert_called_once_with('/merchant/subscriptions/list', self.new_api, {'seckey': 'dummy'}) 37 | self.assertTrue(response) 38 | 39 | @patch('ravepaypysdk.resources.Create.create') 40 | def test_refund(self, mock): 41 | mc = mock.return_value 42 | mc.list.return_value = True 43 | response = Transaction.refund(self.payload, api=self.new_api) 44 | mock.assert_called_once_with('/gpx/merchant/transactions/refund', self.new_api, 45 | {'error': False, 'Payment': 1200, 'id': 2, 'SECKEY': 'dummy'}) 46 | self.assertTrue(response) 47 | 48 | @patch('ravepaypysdk.resources.Create.create') 49 | def test_stop_recurring_payment(self, mock): 50 | mc = mock.return_value 51 | mc.return_value = True 52 | response = Transaction.stop_recurring_payment(self.payload, api=self.new_api) 53 | mock.assert_called_once_with("/merchant/subscriptions/stop", self.new_api, 54 | {'seckey': 'dummy', 'error': False, 'Payment': 1200, 'id': 2}) 55 | self.assertTrue(response) 56 | 57 | def test_stop_recurring_payment_exception(self): 58 | payload = dict(error=False, ) 59 | self.assertRaises(KeyError, Transaction.stop_recurring_payment, payload, api=self.new_api) 60 | 61 | @patch('ravepaypysdk.resources.Create.create') 62 | def test_verify_transaction(self, mock): 63 | path = '/flwv3-pug/getpaidx/api/verify' 64 | mc = mock.return_value 65 | mc.return_value = True 66 | verify_response = Transaction.verify(self.payload, api=self.new_api) 67 | mock.assert_called_once_with(path, self.new_api, { 68 | 'error': False, 'Payment': 1200, 'id': 2, 'SECKEY': 'dummy'}) 69 | self.assertTrue(verify_response) 70 | 71 | @patch('ravepaypysdk.resources.Create.create') 72 | def test_xquery_verify_transaction(self, mock): 73 | path = '/flwv3-pug/getpaidx/api/xrequery' 74 | xquery_response = Transaction.verify_query(self.payload, api=self.new_api) 75 | mock.assert_called_once_with( 76 | path, self.new_api, { 77 | 'error': False, 'Payment': 1200, 'id': 2, 'SECKEY': 'dummy'} 78 | ) 79 | self.assertTrue(xquery_response) 80 | 81 | 82 | class TestPayment(unittest.TestCase): 83 | def setUp(self): 84 | self.new_api = Api( 85 | secret_key=os.environ.get('secret_key'), 86 | public_key='dummy', 87 | production=False 88 | ) 89 | self.path = '/flwv3-pug/getpaidx/api/charge' 90 | self.card_payload = dict(error=False, cardno='34343', cvv='232', email='jb@gmail.com') 91 | self.bank_payload = dict(error=False, accountnumber='34343', accountbank='232') 92 | self.mpesa_payload = { 93 | 'payment-type': 'mpesa', 94 | 'is_mpesa': 1 95 | } 96 | self.gh_mobile_payload = { 97 | 'payment-type': 'mobilemoneygh', 98 | 'is_mobile_money_gh': 1 99 | } 100 | self.ussd_payload = { 101 | 'payment_type': 'ussd', 102 | 'is_ussd': 1 103 | } 104 | 105 | @patch('ravepaypysdk.resources.Create.create') 106 | def test_card_direct_payment(self, mock): 107 | Payment.card(self.card_payload, api=self.new_api) 108 | mock.assert_called_once_with(self.path, self.new_api, 109 | { 110 | 'client': b'Zah+qD4JviOdUFttZL7d0MgB/LPtEW4Gz7werMMDtOIWZLCGeZ7hRb0PlZbPU6JfELIgpyUP/L9ylb58B6EpXGPYHhYjC/Uza94nb4ZLiJM=', 111 | 'PBFPubKey': 'dummy', 'alg': '3DES-24'}) 112 | 113 | def test_card_payment_exception(self): 114 | self.assertRaises(KeyError, Payment.card, self.bank_payload, api=self.new_api) 115 | 116 | @patch('ravepaypysdk.resources.Create.create') 117 | def test_bank_account_direct_payment(self, mock): 118 | Payment.bank_account(self.bank_payload, api=self.new_api) 119 | mock.assert_called_once_with(self.path, self.new_api, 120 | { 121 | 'client': b'Zah+qD4JviOdUFttZL7d0D1iXdxyTXxBCbWZvXCfmUrkhNr7MxySJLe/01oZZxph27HEjbbOZVJCsD+WMA8Cjj78w8CcOF0t', 122 | 'PBFPubKey': 'dummy', 'alg': '3DES-24'}) 123 | 124 | def test_bank_account_payment_exception(self): 125 | self.assertRaises(KeyError, Payment.bank_account, self.card_payload, api=self.new_api) 126 | 127 | @patch('ravepaypysdk.resources.Create.create') 128 | def test_mpesa_direct_payment(self, mock): 129 | Payment.mpesa(self.mpesa_payload, self.new_api) 130 | mock.assert_called_once_with(self.path, self.new_api, 131 | { 132 | 'client': b'BX1KArxf7xAngqqNs/uWpMcxilwH0CmJ0pMwxLLFPtXClSO3y4oSqD78w8CcOF0t', 133 | 'PBFPubKey': 'dummy', 'alg': '3DES-24'}) 134 | 135 | def test_mpesa_payment_exception(self): 136 | self.assertRaises(KeyError, Payment.mpesa, self.card_payload, api=self.new_api) 137 | 138 | @patch('ravepaypysdk.resources.Create.create') 139 | def test_gh_money_payment(self, mock): 140 | Payment.ghana_mobile(self.gh_mobile_payload, self.new_api) 141 | mock.assert_called_once_with(self.path, self.new_api, 142 | { 143 | 'client': b'BX1KArxf7xAngqqNs/uWpF1xwWg3A0stKlGYVIidO/hNNG86I+JiSE7q7FFVlKolnQv3RdAjAqQu0ZCzmKFRog==', 144 | 'PBFPubKey': 'dummy', 'alg': '3DES-24'}) 145 | 146 | def test_gh_money_payment_exception(self): 147 | self.assertRaises(KeyError, Payment.ghana_mobile, self.card_payload, api=self.new_api) 148 | 149 | @patch('ravepaypysdk.resources.Create.create') 150 | def test_ussd_payment(self, mock): 151 | Payment.ussd(self.ussd_payload, self.new_api) 152 | mock.assert_called_once_with(self.path, self.new_api, 153 | { 154 | 'client': b'BX1KArxf7xCQvJirHHQBNK1AAZ9qzlza9vNv8GjMkIqsM6leOIFazg==', 155 | 'PBFPubKey': 'dummy', 'alg': '3DES-24'}) 156 | 157 | def test_ussd_payment_exception(self): 158 | self.assertRaises(KeyError, Payment.ussd, self.card_payload, api=self.new_api) 159 | 160 | @patch('ravepaypysdk.resources.Create.create') 161 | def test_tokenize_card(self, mock): 162 | path = 'flwv3-pug/getpaidx/api/tokenized/charge' 163 | Payment.tokenize_card(self.card_payload, self.new_api) 164 | mock.assert_called_once_with(path, self.new_api, 165 | {'SECKEY': 'FLWSECK-cc8399cf35c2d1cfb62dd44c3c13f9ab-X', 'error': False, 166 | 'cardno': '34343', 'cvv': '232', 'email': 'jb@gmail.com'}) 167 | 168 | def test_tokenize_exception(self): 169 | self.assertRaises(KeyError, Payment.tokenize_card, self.mpesa_payload, api=self.new_api) 170 | 171 | class TestBank(unittest.TestCase): 172 | def setUp(self): 173 | self.api = Api( 174 | secret_key=os.environ.get('secret_key'), 175 | public_key='dummy', 176 | production=False 177 | ) 178 | 179 | @patch('ravepaypysdk.resources.List.list') 180 | def test_get_all_banks(self, mock): 181 | path = '/flwv3-pug/getpaidx/api/flwpbf-banks.js?json=1' 182 | 183 | Bank.list_all(api=self.api) 184 | mock.assert_called_once_with(path, self.api) 185 | 186 | @patch('ravepaypysdk.resources.Create.create') 187 | def test_get_forex(self, mock): 188 | path = '/flwv3-pug/getpaidx/api/forex' 189 | payload = { 190 | 'origin_currency': 'USD', 191 | 'destination_currency': 'NGN', 192 | 'amount': '200' 193 | } 194 | Bank.get_forex(payload, api=self.api) 195 | mock.assert_called_once_with( 196 | path, self.api, { 197 | 'origin_currency': 'USD', 'destination_currency': 'NGN', 'amount': '200', 198 | 'SECKEY': 'FLWSECK-cc8399cf35c2d1cfb62dd44c3c13f9ab-X'} 199 | ) 200 | 201 | 202 | class TestPreauthorization(unittest.TestCase): 203 | def setUp(self): 204 | self.api = Api( 205 | secret_key=os.environ.get('secret_key'), 206 | public_key='dummy', 207 | production=False 208 | ) 209 | 210 | @patch('ravepaypysdk.resources.Create.create') 211 | def test_preauthorize_card(self, mock): 212 | path = '/flwv3-pug/getpaidx/api/charge' 213 | payload = dict(error=False, cardno='34343', cvv='232', email='jb@gmail.com') 214 | 215 | PreAuthorization.preauthorize_card(payload, api=self.api) 216 | mock.assert_called_once_with( 217 | path, self.api, { 218 | 'client': b'Zah+qD4JviOdUFttZL7d0MgB/LPtEW4Gz7werMMDtOIWZLCGeZ7hRb0PlZbPU6JfELIgpyUP/L9ylb58B6EpXGPYHhYjC/Uza94nb4ZLiJM=', 219 | 'PBFPubKey': 'dummy', 'alg': '3DES-24'} 220 | ) 221 | 222 | @patch('ravepaypysdk.resources.Create.create') 223 | def test_capture(self, mock_capture): 224 | path = '/flwv3-pug/getpaidx/api/capture' 225 | payload = dict(error=False, email='jb@gmail.com') 226 | 227 | PreAuthorization.capture(payload, api=self.api) 228 | mock_capture.assert_called_once_with( 229 | path, self.api, { 230 | 'error': False, 'email': 'jb@gmail.com', 231 | 'SECKEY': 'FLWSECK-cc8399cf35c2d1cfb62dd44c3c13f9ab-X'} 232 | ) 233 | 234 | @patch('ravepaypysdk.helpers.Create.create') 235 | def test_void_refund(self, mock): 236 | path = '/flwv3-pug/getpaidx/api/refundorvoid' 237 | payload = dict(error=False, email='jb@gmail.com') 238 | 239 | PreAuthorization.void_or_refund(payload, api=self.api) 240 | mock.assert_called_once_with( 241 | path, self.api, 242 | {'error': False, 'email': 'jb@gmail.com', 'SECKEY': 'FLWSECK-cc8399cf35c2d1cfb62dd44c3c13f9ab-X'} 243 | ) 244 | 245 | 246 | class TestValidateCharge(unittest.TestCase): 247 | def setUp(self): 248 | self.api = Api( 249 | secret_key=os.environ.get('secret_key'), 250 | public_key='dummy', 251 | production=False 252 | ) 253 | self.payload = dict(error=False, cardno='34343', cvv='232', email='jb@gmail.com') 254 | self.card_path = '/flwv3-pug/getpaidx/api/validatecharge' 255 | self.account_path = '/flwv3-pug/getpaidx/api/validate' 256 | 257 | @patch('ravepaypysdk.helpers.Create.create') 258 | def test_card_validation(self, mock): 259 | ValidateCharge.card(self.payload, api=self.api) 260 | mock.assert_called_once_with( 261 | self.card_path, self.api, { 262 | 'error': False, 'cardno': '34343', 'cvv': '232', 263 | 'email': 'jb@gmail.com', 'PBFPubKey': 'dummy'} 264 | ) 265 | 266 | @patch('ravepaypysdk.helpers.Create.create') 267 | def test_account_validation(self, mock): 268 | ValidateCharge.account(self.payload, api=self.api) 269 | mock.assert_called_once_with( 270 | self.account_path, self.api, { 271 | 'error': False, 'cardno': '34343', 'cvv': '232', 272 | 'email': 'jb@gmail.com', 'PBFPubKey': 'dummy'} 273 | ) 274 | 275 | 276 | class TestPaymentPlan(unittest.TestCase): 277 | def setUp(self): 278 | self.api = Api( 279 | secret_key=os.environ.get('secret_key'), 280 | public_key='dummy', 281 | production=False 282 | ) 283 | self.payload = dict(id=70, name='johnbosco ohia') 284 | self.params = dict(id=70) 285 | self.plan_id = 20 286 | 287 | 288 | @patch('ravepaypysdk.helpers.Create.create') 289 | def test_create_payment_plan(self, mock): 290 | path = 'v2/gpx/paymentplans/create' 291 | PaymentPlan.create_plan(self.payload, api=self.api) 292 | mock.assert_called_once_with( 293 | path, self.api, { 294 | 'id': 70, 'name': 'johnbosco ohia','seckey': self.api.secret_key 295 | }) 296 | 297 | @patch('ravepaypysdk.helpers.List.list') 298 | def test_fetch_all_payment_plans(self, mock): 299 | path = 'v2/gpx/paymentplans/query' 300 | PaymentPlan.fetch_all_plan(api=self.api) 301 | mock.assert_called_once_with( 302 | path, self.api, { 303 | 'seckey': self.api.secret_key 304 | } 305 | ) 306 | 307 | @patch('ravepaypysdk.helpers.List.list') 308 | def test_fetch_single_payment(self, mock): 309 | path = 'v2/gpx/paymentplans/query' 310 | PaymentPlan.fetch_single_plan(self.params, api=self.api) 311 | mock.assert_called_once_with( 312 | path, self.api, { 313 | 'id': 70, 'seckey': self.api.secret_key 314 | } 315 | ) 316 | 317 | @patch('ravepaypysdk.helpers.Create.create') 318 | def test_edit_plan(self, mock): 319 | path = 'v2/gpx/paymentplans/{}/edit'.format(self.plan_id) 320 | PaymentPlan.edit_plan(self.payload, self.plan_id, api=self.api) 321 | mock.assert_called_once_with( 322 | path, self.api, { 323 | 'id': 70, 'name': 'johnbosco ohia', 'seckey': self.api.secret_key 324 | } 325 | ) 326 | 327 | @patch('ravepaypysdk.helpers.Create.create') 328 | def test_edit_plan_without_payload(self, mock): 329 | path = 'v2/gpx/paymentplans/{}/edit'.format(self.plan_id) 330 | PaymentPlan.edit_plan(payload=None, plan_id=self.plan_id, api=self.api) 331 | mock.assert_called_once_with( 332 | path, self.api, { 333 | 'seckey': self.api.secret_key 334 | } 335 | ) 336 | 337 | @patch('ravepaypysdk.helpers.Create.create') 338 | def test_cancel_payment_plan(self, mock): 339 | path = 'v2/gpx/paymentplans/{}/cancel'.format(self.plan_id) 340 | PaymentPlan.cancel_plan(self.plan_id, api=self.api) 341 | mock.assert_called_once_with( 342 | path, self.api, { 343 | 'seckey': self.api.secret_key 344 | } 345 | ) 346 | 347 | def test_invalid_cancel_payment_plan(self): 348 | invalid_cancel_sub = PaymentPlan.cancel_plan(plan_id=None, api=self.api) 349 | self.assertEqual(None, invalid_cancel_sub) 350 | 351 | 352 | class TestSubscriptions(unittest.TestCase): 353 | 354 | def setUp(self): 355 | self.api = Api( 356 | secret_key=os.environ.get('secret_key'), 357 | public_key='dummy', 358 | production=False 359 | ) 360 | self.payload = dict(id=70, name='johnbosco ohia') 361 | self.params = dict(id=70) 362 | self.sub_id = 20 363 | 364 | 365 | @patch('ravepaypysdk.helpers.List.list') 366 | def test_fetch_all_subscriptions(self, mock): 367 | path = 'v2/gpx/subscriptions/query' 368 | Subscriptions.fetch_all(api=self.api) 369 | mock.assert_called_once_with( 370 | path, self.api, { 371 | 'seckey': self.api.secret_key 372 | } 373 | ) 374 | @patch('ravepaypysdk.helpers.List.list') 375 | def test_fetch_single_subscription(self, mock): 376 | path = 'v2/gpx/subscriptions/query' 377 | Subscriptions.fetch_single(self.params, api=self.api) 378 | mock.assert_called_once_with( 379 | path, self.api, { 380 | 'id': 70, 'seckey': self.api.secret_key 381 | } 382 | ) 383 | 384 | def test_invalid_fetch_single_subscription(self): 385 | invalid_fetch_single_sub = Subscriptions.fetch_single(params=None, api=self.api) 386 | self.assertEqual(None, invalid_fetch_single_sub) 387 | 388 | @patch('ravepaypysdk.helpers.Create.create') 389 | def test_cancel_subscription(self, mock): 390 | path = 'v2/gpx/subscriptions/{}/cancel'.format(self.sub_id) 391 | Subscriptions.cancel(sub_id=self.sub_id, api=self.api) 392 | mock.assert_called_once_with( 393 | path, self.api, { 394 | 'seckey': self.api.secret_key 395 | } 396 | ) 397 | 398 | def test_invalid_cancel_subscription(self): 399 | invalid_cancel_sub = Subscriptions.cancel(sub_id=None, api=self.api) 400 | self.assertEqual(None, invalid_cancel_sub) 401 | 402 | 403 | @patch('ravepaypysdk.helpers.Create.create') 404 | def test_activate_subscription(self, mock): 405 | path = 'v2/gpx/subscriptions/{}/activate'.format(self.sub_id) 406 | Subscriptions.activate(sub_id=self.sub_id, api=self.api) 407 | mock.assert_called_once_with( 408 | path, self.api, { 409 | 'seckey': self.api.secret_key 410 | } 411 | ) 412 | 413 | def test_invalid_activate_subscription(self): 414 | invalid_activate_sub = Subscriptions.activate(sub_id=None, api=self.api) 415 | self.assertEqual(None, invalid_activate_sub) 416 | 417 | 418 | 419 | 420 | 421 | 422 | --------------------------------------------------------------------------------