├── .gitignore ├── .travis.yml ├── README.md ├── VERSION ├── examples ├── bank_accounts.py ├── card_example.py ├── charges_example.py ├── customer_example.py ├── fees.py ├── payouts.py ├── plans_example.py ├── test_create_customer.py ├── test_customer_charge_bank.py ├── test_list_customer.py ├── test_merchant_charge_bank.py ├── test_merchant_charge_card.py └── transfers.py ├── openpay ├── __init__.py ├── api.py ├── error.py ├── http_client.py ├── resource.py ├── test │ ├── __init__.py │ ├── helper.py │ ├── test_integration.py │ └── test_resources.py ├── testCo │ ├── __init__.py │ ├── helperco.py │ ├── test_co_integration.py │ └── test_co_resources.py ├── testPe │ ├── __init__.py │ ├── helperpe.py │ ├── test_pe_integration.py │ └── test_pe_resources.py ├── util.py └── version.py └── setup.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime* 2 | *.pyc 3 | *.egg* 4 | *.bak 5 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | python: 3 | - "2.7" 4 | - "pypy" 5 | - "3.3" 6 | before_install: 7 | - sudo apt-get update 8 | - sudo apt-get install libcurl4-gnutls-dev librtmp-dev 9 | install: 10 | - pip install pycurl flake8 11 | - pip install mock 12 | - python setup.py install 13 | script: 14 | - python -W always setup.py test 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Openpay Python](https://www.openpay.mx/img/github/python.jpg) 2 | 3 | Python Client for Openpay API Services 4 | 5 | This is a client implementing the payment services for Openpay at www.openpay.mx 6 | 7 | Installation 8 | ============= 9 | 10 | You don't need this source code unless you want to modify the package. If you just want use the Openpay Python bindings, 11 | you should run: 12 | 13 | pip install "setuptools<58.0" 14 | pip install openpay 15 | 16 | or 17 | 18 | pip install openpay --udgrade 19 | 20 | See www.pip-installer.org/en/latest/index.html for instructions on installing pip. 21 | 22 | 23 | Implementation 24 | ============== 25 | 26 | 27 | 28 | Usage for México 29 | ============== 30 | 31 | #### Configuration #### 32 | 33 | Before use the library will be necessary to set up your Merchant ID and Private key. 34 | 35 | ```python 36 | import openpay 37 | 38 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 39 | openpay.verify_ssl_certs = False 40 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 41 | openpay.production = True # By default this works in sandbox mode 42 | openpay.country = 'mx' # 'mx' is default value, to use for Colombia set country='co' 43 | ``` 44 | 45 | Once configured the library, you can use it to interact with Openpay API services. 46 | 47 | ##### Tokens ##### 48 | 49 | Creating a token: 50 | 51 | ```python 52 | openpay.Token.create( 53 | card_number="4111111111111111", 54 | holder_name="Juan Perez Ramirez", 55 | expiration_year="20", 56 | expiration_month="12", 57 | cvv2="110", 58 | address={ 59 | "city": "Querétaro", 60 | "country_code": "MX", 61 | "postal_code": "76900", 62 | "line1": "Av 5 de Febrero", 63 | "line2": "Roble 207", 64 | "line3": "col carrillo", 65 | "state": "Queretaro" 66 | }) 67 | ``` 68 | 69 | ##### Customer ##### 70 | 71 | Creating a customer: 72 | 73 | ```python 74 | customer = openpay.Customer.create( 75 | name="Juan", 76 | email="somebody@example.com", 77 | address={ 78 | "city": "Queretaro", 79 | "state": "Queretaro", 80 | "line1": "Calle de las penas no 10", 81 | "postal_code": "76000", 82 | "line2": "col. san pablo", 83 | "line3": "entre la calle de la alegria y la calle del llanto", 84 | "country_code": "MX" 85 | }, 86 | last_name="Perez", 87 | phone_number="44209087654" 88 | ) 89 | ``` 90 | 91 | Once you have a customer, you have access to few resources for current customer. According to the current version of the 92 | Openpay API, these resources are: 93 | 94 | - cards 95 | - charges 96 | - transfers 97 | - payouts 98 | - bank accounts 99 | - subscriptions 100 | 101 | You can access all of these resources as public variables of the root instance (customer in this example), so, if you 102 | want to add a new card you will be able to do it as follows: 103 | 104 | ```python 105 | card = customer.cards.create( 106 | card_number="4111111111111111", 107 | holder_name="Juan Perez Ramirez", 108 | expiration_year="20", 109 | expiration_month="12", 110 | cvv2="110", 111 | address={ 112 | "city": "Querétaro", 113 | "country_code": "MX", 114 | "postal_code": "76900", 115 | "line1": "Av 5 de Febrero", 116 | "line2": "Roble 207", 117 | "line3": "col carrillo", 118 | "state": "Queretaro" 119 | }) 120 | ``` 121 | 122 | Get a customer 123 | 124 | ```python 125 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 126 | ``` 127 | 128 | Update a customer 129 | 130 | ```python 131 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 132 | customer.last_name = "Lopez" 133 | customer.save() 134 | ``` 135 | 136 | Get all customers 137 | 138 | ```python 139 | customers = openpay.Customer.all() 140 | ``` 141 | 142 | ###### Customer Cards ###### 143 | 144 | Get all customer cards 145 | 146 | ```python 147 | cards = customer.cards.all() 148 | ``` 149 | 150 | Get specific customer card 151 | 152 | ```python 153 | card = customer.cards.retrieve('kvxvccpsesm4pwmtgnjb') 154 | ``` 155 | 156 | Delete a customer card 157 | 158 | ```python 159 | card = customer.cards.retrieve('kvxvccpsesm4pwmtgnjb') 160 | card.delete() 161 | ``` 162 | 163 | ###### Customer Transfers ###### 164 | 165 | Get all customer transfers (inbound and outbound) 166 | 167 | ```python 168 | transfers = customer.transfers.all() 169 | ``` 170 | 171 | Create a customer transfer 172 | 173 | ```python 174 | transfer1 = customer.transfers.create( 175 | customer_id="acuqxruyv0hi1wfdwmym", 176 | amount=100, 177 | description="Test transfer", 178 | order_id="oid-00059" 179 | ) 180 | ``` 181 | 182 | Get specific transfer 183 | 184 | ```python 185 | transfer2 = customer.transfers.retrieve(transfer1.id) 186 | ``` 187 | 188 | ###### Bank Accounts ###### 189 | 190 | Add bank account to customer 191 | 192 | ```python 193 | bank_account = customer.bank_accounts.create( 194 | clabe="032180000118359719", 195 | alias="Cuenta principal", 196 | holder_name="Juan Perez" 197 | ) 198 | ``` 199 | 200 | Get all customer bank accounts 201 | 202 | ```python 203 | accounts = customer.bank_accounts.all() 204 | ``` 205 | 206 | Get specific bank account 207 | 208 | ```python 209 | account = customer.back_accounts.retrieve("bsbg7igxh3yukpu8t2q4") 210 | ``` 211 | 212 | ###### Subscriptions ###### 213 | 214 | Add subscription to customer 215 | 216 | ```python 217 | customer.subscriptions.create(plan_id="pbkliysxavp8bvvp8f0k", trial_days="5", card_id="kvxvccpsesm4pwmtgnjb") 218 | ``` 219 | 220 | Cancel subscription 221 | 222 | ```python 223 | subscription = customer.subscriptions.all()[0] 224 | subscription.delete() 225 | ``` 226 | 227 | List all customers subscriptions 228 | 229 | ```python 230 | customer.subscriptions.all() 231 | ``` 232 | 233 | Update subscription 234 | 235 | ```python 236 | subscription = customer.subscriptions.all()[0] 237 | subscription.cancel_at_end_period = True 238 | subscription.save() 239 | ``` 240 | 241 | ###### Payouts ###### 242 | 243 | Add payout for customer 244 | 245 | ```python 246 | bank_account = customer.bank_accounts.all().data[0] # We get the first account 247 | customer.payouts.create( 248 | method='bank_account', # possible values ['bank_accunt', 'card'] 249 | destination_id=bank_account.id, 250 | amount="100", 251 | description="First payout", 252 | order_id="oid-00058" 253 | ) 254 | ``` 255 | 256 | Get all payouts 257 | 258 | ```python 259 | customer.payouts.all() 260 | ``` 261 | 262 | Get specific payout 263 | 264 | ```python 265 | customer.payouts.retrieve("tbs6a7g4pypww4eq640d") 266 | ``` 267 | 268 | ##### Plan ##### 269 | 270 | Create new plan 271 | 272 | ```python 273 | plan = openpay.Plan.create( 274 | amount=150.00, 275 | status_after_retry="cancelled", 276 | retry_times=2, 277 | name="Curso de Ingles", 278 | repeat_unit="month", 279 | trial_days=30, 280 | repeat_every=1 281 | ) 282 | ``` 283 | 284 | Get specific plan 285 | 286 | ```python 287 | plan2 = openpay.Plan.retrieve(plan.id) 288 | ``` 289 | 290 | Update a plan 291 | 292 | ```python 293 | plan = openpay.Plan.retrieve('pbkliysxavp8bvvp8f0k') 294 | plan.name = "Curso de Ingles II" 295 | plan.save() 296 | ``` 297 | 298 | Delete plan 299 | 300 | ```python 301 | plan = openpay.Plan.retrieve('pbkliysxavp8bvvp8f0k') 302 | plan.delete() 303 | ``` 304 | 305 | ##### Fee ##### 306 | 307 | You may charge a fee as follows: 308 | 309 | ```python 310 | fee = openpay.Fee.create( 311 | customer_id="amce5ycvwycfzyarjf8l", 312 | amount=12.50, 313 | description="Fee Charge", 314 | order_id="oid=1245" 315 | ) 316 | ```` 317 | 318 | List all charged fees 319 | 320 | ```python 321 | fees = openpay.Fee.all() 322 | ``` 323 | 324 | Usage for Perú 325 | ============== 326 | 327 | #### Configuration #### 328 | 329 | Before use the library will be necessary to set up your Merchant ID and Private key. 330 | 331 | ```python 332 | import openpay 333 | 334 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 335 | openpay.verify_ssl_certs = False 336 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 337 | openpay.production = True # By default this works in sandbox mode 338 | openpay.country = 'pe' # 'mx' is default value, to use for Peru set country='pe' 339 | ``` 340 | Once configured the library, you can use it to interact with Openpay API services. 341 | 342 | ##### Tokens ##### 343 | 344 | Creating a token: 345 | 346 | ```python 347 | openpay.Token.create( 348 | card_number="4111111111111111", 349 | holder_name="Juan Perez Ramirez", 350 | expiration_year="20", 351 | expiration_month="12", 352 | cvv2="110", 353 | address={ 354 | "city": "Lima", 355 | "country_code": "PE", 356 | "postal_code": "15076", 357 | "line1": "Av 5 de Febrero", 358 | "line2": "Roble 207", 359 | "line3": "Lince", 360 | "state": "Lima" 361 | }) 362 | ``` 363 | 364 | ##### Customer ##### 365 | 366 | Creating a customer: 367 | 368 | ```python 369 | customer = openpay.Customer.create( 370 | name="Juan", 371 | email="somebody@example.com", 372 | address={ 373 | "city": "Lima", 374 | "country_code": "PE", 375 | "postal_code": "15076", 376 | "line1": "Av 5 de Febrero", 377 | "line2": "Roble 207", 378 | "line3": "Lince", 379 | "state": "Lima" 380 | }, 381 | last_name="Perez", 382 | phone_number="44209087654" 383 | ) 384 | ``` 385 | 386 | Once you have a customer, you have access to few resources for current customer. According to the current version of the 387 | Openpay API, these resources are: 388 | 389 | - cards 390 | - charges 391 | - transfers 392 | - payouts 393 | - bank accounts 394 | - subscriptions 395 | 396 | You can access all of these resources as public variables of the root instance (customer in this example), so, if you 397 | want to add a new card you will be able to do it as follows: 398 | 399 | ```python 400 | card = customer.cards.create( 401 | card_number="4111111111111111", 402 | holder_name="Juan Perez Ramirez", 403 | expiration_year="20", 404 | expiration_month="12", 405 | cvv2="110", 406 | address={ 407 | "city": "Lima", 408 | "country_code": "PE", 409 | "postal_code": "15076", 410 | "line1": "Av 5 de Febrero", 411 | "line2": "Roble 207", 412 | "line3": "Lince", 413 | "state": "Lima" 414 | }) 415 | ``` 416 | 417 | Get a customer 418 | 419 | ```python 420 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 421 | ``` 422 | 423 | Update a customer 424 | 425 | ```python 426 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 427 | customer.last_name = "Lopez" 428 | customer.save() 429 | ``` 430 | 431 | Get all customers 432 | 433 | ```python 434 | customers = openpay.Customer.all() 435 | ``` 436 | 437 | ###### Customer Cards ###### 438 | 439 | Get all customer cards 440 | 441 | ```python 442 | cards = customer.cards.all() 443 | ``` 444 | 445 | Get specific customer card 446 | 447 | ```python 448 | card = customer.cards.retrieve('kvxvccpsesm4pwmtgnjb') 449 | ``` 450 | 451 | Delete a customer card 452 | 453 | ```python 454 | card = customer.cards.retrieve('kvxvccpsesm4pwmtgnjb') 455 | card.delete() 456 | ``` 457 | 458 | ###### Customer Transfers ###### 459 | 460 | Get all customer transfers (inbound and outbound) 461 | 462 | ```python 463 | transfers = customer.transfers.all() 464 | ``` 465 | 466 | Create a customer transfer 467 | 468 | ```python 469 | transfer1 = customer.transfers.create( 470 | customer_id="acuqxruyv0hi1wfdwmym", 471 | amount=100, 472 | description="Test transfer", 473 | order_id="oid-00059" 474 | ) 475 | ``` 476 | 477 | Get specific transfer 478 | 479 | ```python 480 | transfer2 = customer.transfers.retrieve(transfer1.id) 481 | ``` 482 | 483 | ###### Bank Accounts ###### 484 | 485 | Add bank account to customer 486 | 487 | ```python 488 | bank_account = customer.bank_accounts.create( 489 | clabe="032180000118359719", 490 | alias="Cuenta principal", 491 | holder_name="Juan Perez" 492 | ) 493 | ``` 494 | 495 | Get all customer bank accounts 496 | 497 | ```python 498 | accounts = customer.bank_accounts.all() 499 | ``` 500 | 501 | Get specific bank account 502 | 503 | ```python 504 | account = customer.back_accounts.retrieve("bsbg7igxh3yukpu8t2q4") 505 | ``` 506 | 507 | ###### Subscriptions ###### 508 | 509 | Add subscription to customer 510 | 511 | ```python 512 | customer.subscriptions.create(plan_id="pbkliysxavp8bvvp8f0k", trial_days="5", card_id="kvxvccpsesm4pwmtgnjb") 513 | ``` 514 | 515 | Cancel subscription 516 | 517 | ```python 518 | subscription = customer.subscriptions.all()[0] 519 | subscription.delete() 520 | ``` 521 | 522 | List all customers subscriptions 523 | 524 | ```python 525 | customer.subscriptions.all() 526 | ``` 527 | 528 | Update subscription 529 | 530 | ```python 531 | subscription = customer.subscriptions.all()[0] 532 | subscription.cancel_at_end_period = True 533 | subscription.save() 534 | ``` 535 | 536 | ###### Checkouts ###### 537 | 538 | Add checkout to customer 539 | 540 | ```python 541 | customer.checkouts.create( 542 | { 543 | "amount": 250, 544 | "currency": "PEN", 545 | "description": "Cargo cobro con link", 546 | "redirect_url": "https://misitioempresa.pe", 547 | "order_id": "oid-12331", 548 | "expiration_date": "2021-08-31 12:50", 549 | "send_email": True 550 | }) 551 | ``` 552 | 553 | List all customers checkouts 554 | 555 | ```python 556 | customer.checkouts.all() 557 | ``` 558 | 559 | Update checkout 560 | 561 | ```python 562 | subscription = customer.subscriptions.all()[0] 563 | subscription.cancel_at_end_period = True 564 | subscription.save() 565 | ``` 566 | 567 | ##### Plan ###### 568 | 569 | Create new plan 570 | 571 | ```python 572 | plan = openpay.Plan.create( 573 | amount=150.00, 574 | status_after_retry="cancelled", 575 | retry_times=2, 576 | name="Curso de Ingles", 577 | repeat_unit="month", 578 | trial_days=30, 579 | repeat_every=1 580 | ) 581 | ``` 582 | 583 | Get specific plan 584 | 585 | ```python 586 | plan2 = openpay.Plan.retrieve(plan.id) 587 | ``` 588 | 589 | Update a plan 590 | 591 | ```python 592 | plan = openpay.Plan.retrieve('pbkliysxavp8bvvp8f0k') 593 | plan.name = "Curso de Ingles II" 594 | plan.save() 595 | ``` 596 | 597 | Delete plan 598 | 599 | ```python 600 | plan = openpay.Plan.retrieve('pbkliysxavp8bvvp8f0k') 601 | plan.delete() 602 | ``` 603 | 604 | Usage for Colombia 605 | ============== 606 | 607 | ```python 608 | import openpay 609 | 610 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 611 | openpay.verify_ssl_certs = False 612 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 613 | openpay.production = True # By default this works in sandbox mode 614 | openpay.country = 'co' # 'mx' is default value, to use for Colombia set country='co' 615 | ``` 616 | 617 | Once configured the library, you can use it to interact with Openpay API services for Colombia. 618 | 619 | ##### Tokens ##### 620 | 621 | Creating a token: 622 | 623 | ```python 624 | openpay.Token.create( 625 | holder_name="Juan Perez Ramirez", 626 | card_number="4111111111111111", 627 | cvv2="110", 628 | expiration_month="12", 629 | expiration_year="25", 630 | address={ 631 | "city": "Bogotá", 632 | "country_code": "CO", 633 | "postal_code": "76900", 634 | "line1": "bogota", 635 | "line2": "colombia", 636 | "line3": "col carrillo", 637 | "state": "Bogota" 638 | }) 639 | ``` 640 | 641 | ##### Customer ##### 642 | 643 | Creating a customer: 644 | 645 | ```python 646 | customer = openpay.Customer.create( 647 | name="Juan", 648 | last_name="Perez", 649 | email="somebody@example.com", 650 | { 651 | "name": "Pedro Diego", 652 | "last_name": "Alatorre Martínez", 653 | "email": "pedro.alatorre@comercio.com", 654 | "phone_number": "5744484951", 655 | "status": "active", 656 | "customer_address": { 657 | "department": "Medellín", 658 | "city": "Antioquía", 659 | "additional": "Avenida 7f bis # 138-58 Apartamento 942" 660 | } 661 | }, 662 | phone_number="7711234567" 663 | ) 664 | ``` 665 | 666 | Once you have a customer, you have access to few resources for current customer. According to the current version of the 667 | Openpay API, these resources are: 668 | 669 | - cards 670 | - charges 671 | - subscriptions 672 | - pse 673 | 674 | You can access all of these resources as public variables of the root instance (customer in this example), so, if you 675 | want to add a new card you will be able to do it as follows: 676 | 677 | ```python 678 | card = customer.cards.create( 679 | card_number="4111111111111111", 680 | holder_name="Juan Perez Ramirez", 681 | expiration_year="25", 682 | expiration_month="12", 683 | cvv2="110", 684 | address={ 685 | "city": "Bogotá", 686 | "country_code": "CO", 687 | "postal_code": "76900", 688 | "line1": "bogota", 689 | "line2": "colombia", 690 | "line3": "col carrillo", 691 | "state": "Bogota" 692 | }) 693 | ``` 694 | 695 | Get a customer 696 | 697 | ```python 698 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 699 | ``` 700 | 701 | Update a customer 702 | 703 | ```python 704 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 705 | customer.last_name = "Lopez" 706 | customer.save() 707 | ``` 708 | 709 | Get all customers 710 | 711 | ```python 712 | customers = openpay.Customer.all() 713 | ``` 714 | 715 | ###### Customer Cards ###### 716 | 717 | Get all customer cards 718 | 719 | ```python 720 | cards = customer.cards.all() 721 | ``` 722 | 723 | Get specific customer card 724 | 725 | ```python 726 | card = customer.cards.retrieve('kvxvccpsesm4pwmtgnjb') 727 | ``` 728 | 729 | Delete a customer card 730 | 731 | ```python 732 | card = customer.cards.retrieve('kvxvccpsesm4pwmtgnjb') 733 | card.delete() 734 | ``` 735 | 736 | ###### PSE ###### 737 | 738 | Add pse to customer 739 | 740 | ```python 741 | pse = customer.pse.create( 742 | method='bank_account', 743 | currency="COP", 744 | iva="1900", 745 | description="description", 746 | redirect_url="/" 747 | ) 748 | ``` 749 | 750 | ```python 751 | pse = pse.create( 752 | method='bank_account', 753 | currency="COP", 754 | iva="1900", 755 | description="description", 756 | redirect_url="/", 757 | customer={ 758 | "name": "Cliente Colombia", 759 | "last_name": "Vazquez Juarez", 760 | "email": "juan.vazquez@empresa.co", 761 | "phone_number": "4448936475", 762 | "requires_account": False, 763 | "customer_address": { 764 | "department": "Medellín", 765 | "city": "Antioquía", 766 | "additional": "Avenida 7m bis #174-25 Apartamento 637" 767 | } 768 | } 769 | ) 770 | ``` 771 | 772 | ###### Subscriptions ###### 773 | 774 | Add subscription to customer 775 | 776 | ```python 777 | customer.subscriptions.create(plan_id="pbkliysxavp8bvvp8f0k", trial_days="5", card_id="kvxvccpsesm4pwmtgnjb") 778 | ``` 779 | 780 | Cancel subscription 781 | 782 | ```python 783 | subscription = customer.subscriptions.all()[0] 784 | subscription.delete() 785 | ``` 786 | 787 | List all customers subscriptions 788 | 789 | ```python 790 | customer.subscriptions.all() 791 | ``` 792 | 793 | Update subscription 794 | 795 | ```python 796 | subscription = customer.subscriptions.all()[0] 797 | subscription.cancel_at_end_period = True 798 | subscription.save() 799 | ``` 800 | 801 | ##### Plan ##### 802 | 803 | Create new plan 804 | 805 | ```python 806 | plan = openpay.Plan.create( 807 | amount=150.00, 808 | status_after_retry="cancelled", 809 | retry_times=2, 810 | name="Curso de Ingles", 811 | repeat_unit="month", 812 | trial_days=30, 813 | repeat_every=1 814 | ) 815 | ``` 816 | 817 | Get specific plan 818 | 819 | ```python 820 | plan2 = openpay.Plan.retrieve(plan.id) 821 | ``` 822 | 823 | Update a plan 824 | 825 | ```python 826 | plan = openpay.Plan.retrieve('pbkliysxavp8bvvp8f0k') 827 | plan.name = "Curso de Ingles II" 828 | plan.save() 829 | ``` 830 | 831 | Delete plan 832 | 833 | ```python 834 | plan = openpay.Plan.retrieve('pbkliysxavp8bvvp8f0k') 835 | plan.delete() 836 | ``` 837 | 838 | #### Error handling #### 839 | 840 | The Openpay API generates several types of errors depending on the situation, to handle this, the Python client has 841 | implemented four type of exceptions: 842 | 843 | - InvalidRequestError: This category includes requests when format is not JSON and Requests with non existents urls 844 | - AuthenticationError: missing Private key 845 | - CardError: Transfer not accepted, Declined card, Expired card, Inssuficient funds, Stolen Card, Fraudulent card. 846 | - APIError: All other types API errors 847 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 1.01 -------------------------------------------------------------------------------- /examples/bank_accounts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 13 | 14 | print "Listing bank accounts for {0}".format(customer.name) 15 | print customer.bank_accounts.all() 16 | 17 | print "Creating account" 18 | print customer.bank_accounts.create(clabe="646180109490000112", alias="Cuenta principal", holder_name="Carlos Alberto Aguilar") 19 | 20 | print "Listing bank accounts for {0}".format(customer.name) 21 | print customer.bank_accounts.all() 22 | -------------------------------------------------------------------------------- /examples/card_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import datetime 4 | from os import path, pardir 5 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 6 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 7 | 8 | import openpay 9 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 10 | openpay.verify_ssl_certs = False 11 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 12 | 13 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 14 | 15 | print "customer stored cards -------------> ", customer.cards.all().count 16 | 17 | card = customer.cards.create( 18 | card_number="4111111111111111", 19 | holder_name="Juan Perez", 20 | expiration_year="20", 21 | expiration_month="12", 22 | cvv2="110", 23 | address={ 24 | "city":"Querétaro", 25 | "country_code":"MX", 26 | "postal_code":"76900", 27 | "line1":"Av 5 de Febrero", 28 | "line2":"Roble 207", 29 | "line3":"col carrillo", 30 | "state":"Queretaro" 31 | } 32 | ) 33 | print "customer card ----------------->" 34 | print card 35 | 36 | print "customer stored cards -------------> ", customer.cards.all().count 37 | 38 | print "deleting card -------------> ", card.id 39 | card.delete() 40 | print "customer stored cards -------------> ", customer.cards.all().count 41 | 42 | 43 | print "Creating merchant card: " 44 | card = openpay.Card.create( 45 | card_number="4111111111111111", 46 | holder_name="Juan Perez", 47 | expiration_year="20", 48 | expiration_month="12", 49 | cvv2="110", 50 | address={ 51 | "city":"Querétaro", 52 | "country_code":"MX", 53 | "postal_code":"76900", 54 | "line1":"Av 5 de Febrero", 55 | "line2":"Roble 207", 56 | "line3":"col carrillo", 57 | "state":"Queretaro" 58 | } 59 | ) 60 | print "merchant card: ", card 61 | -------------------------------------------------------------------------------- /examples/charges_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | import datetime 4 | from os import path, pardir 5 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 6 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 7 | 8 | import openpay 9 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 10 | openpay.verify_ssl_certs = False 11 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 12 | 13 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 14 | print "customer: ", customer 15 | 16 | card = customer.cards.create( 17 | card_number="4111111111111111", 18 | holder_name="Juan Perez Ramirez", 19 | expiration_year="20", 20 | expiration_month="12", 21 | cvv2="110", 22 | address={ 23 | "city":"Querétaro", 24 | "country_code":"MX", 25 | "postal_code":"76900", 26 | "line1":"Av 5 de Febrero", 27 | "line2":"Roble 207", 28 | "line3":"col carrillo", 29 | "state":"Queretaro" 30 | }) 31 | 32 | print "Card: ", card 33 | 34 | print "Creating card as customer" 35 | charge = customer.charges.create(source_id=card.id, method="card", amount=100, description="Charge", capture=False) 36 | 37 | print "charge: ", charge 38 | 39 | print charge.refund() 40 | 41 | print "Creating card as merchant" 42 | 43 | card = openpay.Card.create( 44 | card_number="4111111111111111", 45 | holder_name="Juan Perez", 46 | expiration_year="20", 47 | expiration_month="12", 48 | cvv2="110", 49 | address={ 50 | "city":"Querétaro", 51 | "country_code":"MX", 52 | "postal_code":"76900", 53 | "line1":"Av 5 de Febrero", 54 | "line2":"Roble 207", 55 | "line3":"col carrillo", 56 | "state":"Queretaro" 57 | } 58 | ) 59 | 60 | print "Card: ", card 61 | 62 | print "Creating charge as merchant" 63 | charge = openpay.Charge.create_as_merchant( 64 | source_id="k2trvya1nxpcytgww4rt", 65 | method="card", amount=100, 66 | description="Fourth charge", 67 | capture=False) 68 | print "charge: ", charge 69 | 70 | print "Retrieve charge with ID as merchant" 71 | charge = openpay.Charge.retrieve_as_merchant(charge.id) 72 | print "charge: ", charge 73 | 74 | print "Capturing charge" 75 | print charge.capture(merchant=True) 76 | 77 | print "Refund charge" 78 | print charge.refund(merchant=True) 79 | 80 | 81 | -------------------------------------------------------------------------------- /examples/customer_example.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from os import path, pardir 3 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 4 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 5 | 6 | import openpay 7 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 8 | openpay.verify_ssl_certs = False 9 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 10 | # cus = openpay.Customer.all() 11 | # print cus 12 | 13 | print "\n Retrieving customer" 14 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 15 | print customer 16 | # print "\n Retrieving customer cards" 17 | # cards = customer.cards.all() 18 | # for card in cards.data: 19 | # print type(card) 20 | # print "Retrieving card with ID: kvxvccpsesm4pwmtgnjb" 21 | # print customer.cards.retrieve('kvxvccpsesm4pwmtgnjb') 22 | # print "\nCreating new customer" 23 | # customer = openpay.Customer.create( 24 | # name="Juan", 25 | # email="somebody@example.com", 26 | # address={ 27 | # "city": "Queretaro", 28 | # "state":"Queretaro", 29 | # "line1":"Calle de las penas no 10", 30 | # "postal_code":"76000", 31 | # "line2":"col. san pablo", 32 | # "line3":"entre la calle de la alegria y la calle del llanto", 33 | # "country_code":"MX" 34 | # }, 35 | # last_name="Perez", 36 | # phone_number="44209087654" 37 | # ) 38 | # print openpay.Customer.all() 39 | print "Displaying all user subscriptions" 40 | print customer.subscriptions.all() 41 | print "Retrieving specific subscription" 42 | print customer.subscriptions.retrieve("sl7zlwys6hxicr8dbhmo") -------------------------------------------------------------------------------- /examples/fees.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | fee = openpay.Fee.create( 13 | customer_id="amce5ycvwycfzyarjf8l", 14 | amount=12.50, 15 | description="Fee Charge" 16 | ) 17 | 18 | print fee 19 | 20 | fee = openpay.Fee.retrieve(fee.id) 21 | 22 | print fee 23 | 24 | refundFee = openpay.Fee.refund(fee.id, description="Fee refund Test") 25 | 26 | print "refund: " , refundFee 27 | 28 | print openpay.Fee.all() 29 | -------------------------------------------------------------------------------- /examples/payouts.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 13 | 14 | print "customer: ", customer 15 | 16 | bank_account = customer.bank_accounts.all().data[0] # We get the first account 17 | 18 | print "bank_account ", bank_account 19 | 20 | payout = customer.payouts.create(method='bank_account', destination_id=bank_account.id, amount="100", description="First payout") 21 | 22 | print "payout ", payout 23 | payoutDeleted = openpay.Payout.delete_as_customer(customer.id, payout.id) 24 | print "payoutDeleted: ", payoutDeleted 25 | 26 | print "Creating payout as merchant" 27 | payout = openpay.Payout.create_as_merchant(method="bank_account", destination_id=bank_account.id, amount=200.00, description="Payout") 28 | print payout 29 | 30 | print "Deleting payout as merchant ---------------->" 31 | payoutDeleted = openpay.Payout.delete_as_merchant(payout.id) 32 | print payoutDeleted 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/plans_example.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | # Creating a plan 12 | #plan = openpay.Plan.create(amount=150.00, status_after_retry="cancelled", retry_times=2, 13 | # name="Curso de Ingles", repeat_unit="month", trial_days=30, repeat_every=1) 14 | #print plan 15 | 16 | # print "Updating plan with ID: pbkliysxavp8bvvp8f0k" 17 | # plan = openpay.Plan.retrieve('pbkliysxavp8bvvp8f0k') 18 | # plan.name="Curso de Ingles II" 19 | # plan.save() 20 | # print "All Plans" 21 | # plans = openpay.Plan.all() 22 | # print plans 23 | 24 | print("Getting customer") 25 | customer = openpay.Customer.retrieve('ancgmuvdtcvppcsfi3j4') 26 | # print customer.cards.create( 27 | # card_number="4111111111111111", 28 | # holder_name="Juan Perez Ramirez", 29 | # expiration_year="20", 30 | # expiration_month="12", 31 | # cvv2="110", 32 | # address={ 33 | # "city":"Querétaro", 34 | # "country_code":"MX", 35 | # "postal_code":"76900", 36 | # "line1":"Av 5 de Febrero", 37 | # "line2":"Roble 207", 38 | # "line3":"col carrillo", 39 | # "state":"Queretaro" 40 | # }) 41 | print "Adding plan to user {0}".format(customer.name) 42 | print('Getting subscription') 43 | subscription = customer.subscriptions.retrieve("stxqkgt48ttknauk0xjx") 44 | 45 | subscription.trial_end_date = "2019-01-11" 46 | subscription.card = None 47 | subscription.source_id = "kmfgttah2vdiyhow5x7r" 48 | 49 | print('Updating subscription') 50 | subscription.save() -------------------------------------------------------------------------------- /examples/test_create_customer.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import datetime 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | newCustomer = openpay.Customer.create( 13 | external_id="AA_00003", 14 | name="Oswaldo", 15 | last_name="Perez", 16 | email="nklnkfv@example.com", 17 | requires_account=False, 18 | phone_number="4429938834", 19 | address={ 20 | "city": "Queretaro", 21 | "state":"Queretaro", 22 | "line1":"Calle de las penas no 10", 23 | "postal_code":"76000", 24 | "line2":"col. san pablo", 25 | "line3":"entre la calle de la alegria y la calle del llanto", 26 | "country_code":"MX" 27 | } 28 | ); 29 | 30 | print(newCustomer); 31 | -------------------------------------------------------------------------------- /examples/test_customer_charge_bank.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import datetime 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | charge = openpay.Charge.create( 13 | customer="atun9ze7n1dvsdraj3fw", 14 | method="bank_account", 15 | amount=100.00, 16 | description="Testing customer charges from python", 17 | order_id="casdcf", 18 | due_date="2015-08-01T00:50:00Z", 19 | metadata={ 20 | "data1":"value1", 21 | "data2":"value2" 22 | } 23 | ); 24 | 25 | print(charge) -------------------------------------------------------------------------------- /examples/test_list_customer.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import datetime 3 | from os import path, pardir 4 | from passlib.tests.utils import limit 5 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 6 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 7 | 8 | import openpay 9 | 10 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 11 | openpay.verify_ssl_certs = False 12 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 13 | 14 | customers = openpay.Customer.all( 15 | external_id="AA_00002", 16 | limit=1 17 | ); 18 | 19 | print(customers); 20 | -------------------------------------------------------------------------------- /examples/test_merchant_charge_bank.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import datetime 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | charge = openpay.Charge.create_as_merchant( 13 | method="bank_account", 14 | amount=100.00, 15 | description="Testing charges from python", 16 | order_id="casdcasdc", 17 | due_date="2015-08-01T00:50:00Z", 18 | customer={ 19 | "name":"Gerry", 20 | "last_name":"Robles", 21 | "email":"gerry@example.com", 22 | "phone_number":"4429938834", 23 | "address":{ 24 | "city": "Queretaro", 25 | "state":"Queretaro", 26 | "line1":"Calle de las penas no 10", 27 | "postal_code":"76000", 28 | "line2":"col. san pablo", 29 | "line3":"entre la calle de la alegria y la calle del llanto", 30 | "country_code":"MX" 31 | } 32 | }, 33 | metadata={ 34 | "data1":"value1", 35 | "data2":"value2" 36 | } 37 | ); 38 | 39 | print(charge) -------------------------------------------------------------------------------- /examples/test_merchant_charge_card.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import datetime 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | 13 | charge = openpay.Charge.create_as_merchant( 14 | method="card", 15 | amount=100.00, 16 | description="Testing card charges from python", 17 | order_id="GHSasd", 18 | device_session_id="kjsadkjnnkjfvknjdfkjnvdkjnfvkj", 19 | card={ 20 | "holder_name":"HOLDER NAME", 21 | "card_number":"4111111111111111", 22 | "cvv2":"123", 23 | "expiration_month":"12", 24 | "expiration_year":"20", 25 | "address":{ 26 | "city": "Queretaro", 27 | "state":"Queretaro", 28 | "line1":"Calle de las penas no 10", 29 | "postal_code":"76000", 30 | "line2":"col. san pablo", 31 | "line3":"entre la calle de la alegria y la calle del llanto", 32 | "country_code":"MX" 33 | } 34 | }, 35 | customer={ 36 | "name":"Heber", 37 | "last_name":"Robles", 38 | "email":"xxxxx@example.com", 39 | "phone_number":"4429938834", 40 | "address":{ 41 | "city": "Queretaro", 42 | "state":"Queretaro", 43 | "line1":"Calle de las penas no 10", 44 | "postal_code":"76000", 45 | "line2":"col. san pablo", 46 | "line3":"entre la calle de la alegria y la calle del llanto", 47 | "country_code":"MX" 48 | } 49 | }, 50 | metadata={ 51 | "data1":"value1", 52 | "data2":"value2" 53 | } 54 | ); 55 | 56 | print(charge) -------------------------------------------------------------------------------- /examples/transfers.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | import sys 3 | from os import path, pardir 4 | PROJECT_ROOT = path.dirname(path.abspath(__file__)) 5 | sys.path.append(path.join(PROJECT_ROOT, pardir)) 6 | 7 | import openpay 8 | openpay.api_key = "sk_10d37cc4da8e4ffd902cdf62e37abd1b" 9 | openpay.verify_ssl_certs = False 10 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 11 | 12 | 13 | customer = openpay.Customer.retrieve('amce5ycvwycfzyarjf8l') 14 | print customer.transfers.create(customer_id="acuqxruyv0hi1wfdwmym", amount=100, description="Test transfer", order_id="oid-00059") 15 | print customer.transfers.all() -------------------------------------------------------------------------------- /openpay/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from future.builtins import str 3 | from openpay.util import logger 4 | api_key = None 5 | merchant_id = None 6 | production = False 7 | api_version = None 8 | verify_ssl_certs = True 9 | country = "mx" 10 | # Resource 11 | 12 | from openpay.resource import ( # noqa 13 | Card, Charge, Customer, Plan, Transfer, 14 | Fee, BankAccount, Payout, Subscription, Pse, Token, Checkout, Webhook) 15 | 16 | # Error imports. Note that we may want to move these out of the root 17 | # namespace in the future and you should prefer to access them via 18 | # the fully qualified `openpay.error` module. 19 | 20 | from openpay.error import ( # noqa 21 | OpenpayError, APIError, APIConnectionError, AuthenticationError, CardError, 22 | InvalidRequestError) 23 | 24 | 25 | #from openpay.resource import ( 26 | # convert_to_openpay_object, BaseObject, APIResource) 27 | #from openpay.util import logger 28 | 29 | import sys as _sys 30 | #import warnings as _warnings 31 | #from inspect import isclass as _isclass, ismodule as _ismodule 32 | 33 | _dogetattr = object.__getattribute__ 34 | _ALLOWED_ATTRIBUTES = ( 35 | 'api_key', 36 | 'api_base', 37 | 'api_version', 38 | 'verify_ssl_certs', 39 | 'TEST_MODE', 40 | ) 41 | _original_module = _sys.modules[__name__] 42 | 43 | 44 | def get_api_base(): 45 | api_base = None 46 | if country is None or (country != "mx" and country != "co" and country != "pe"): 47 | errorMessage = "Country is " + country + ", you can set country with value 'mx', 'co' or 'pe', Mexico, Colombia or Peru respectively" 48 | logger.error(errorMessage) 49 | raise error.InvalidCountryError(errorMessage, None, None, 400, None) 50 | if country == "mx": 51 | logger.info("Country Mexico") 52 | if not production: 53 | api_base = str("https://sandbox-api.openpay.mx") 54 | else: 55 | api_base = str("https://api.openpay.mx") 56 | if country == "co": 57 | logger.info("Country Mexico") 58 | if not production: 59 | api_base = str("https://sandbox-api.openpay.co") 60 | else: 61 | api_base = str("https://api.openpay.co") 62 | if country == "pe": 63 | logger.info("Country Peru") 64 | if not production: 65 | api_base = str("https://sandbox-api.openpay.pe") 66 | else: 67 | api_base = str("https://api.openpay.pe") 68 | return api_base 69 | -------------------------------------------------------------------------------- /openpay/api.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import calendar 3 | import time 4 | # import warnings 5 | import platform 6 | import json 7 | 8 | import openpay 9 | from openpay import error, http_client, version, util 10 | 11 | 12 | def _encode_datetime(dttime): 13 | if dttime.tzinfo and dttime.tzinfo.utcoffset(dttime) is not None: 14 | utc_timestamp = calendar.timegm(dttime.utctimetuple()) 15 | else: 16 | utc_timestamp = time.mktime(dttime.timetuple()) 17 | 18 | return int(utc_timestamp) 19 | 20 | 21 | def _api_encode(data): 22 | for key, value in data.iteritems(): 23 | key = util.utf8(key) 24 | if value is None: 25 | continue 26 | elif hasattr(value, 'openpay_id'): 27 | yield (key, value.openpay_id) 28 | elif isinstance(value, list) or isinstance(value, tuple): 29 | for subvalue in value: 30 | yield ("%s[]" % (key,), util.utf8(subvalue)) 31 | elif isinstance(value, dict): 32 | subdict = dict(('%s[%s]' % (key, subkey), subvalue) for 33 | subkey, subvalue in value.iteritems()) 34 | for subkey, subvalue in _api_encode(subdict): 35 | yield (subkey, subvalue) 36 | elif isinstance(value, datetime.datetime): 37 | yield (key, _encode_datetime(value)) 38 | else: 39 | yield (key, util.utf8(value)) 40 | 41 | 42 | def _build_api_url(url, query): 43 | if (len(query) > 0): 44 | path = url + "?" 45 | for key in query: 46 | if (isinstance(query[key], dict)): 47 | for x in query[key]: 48 | path = path + key + "[" + x + "]=" + query[key][x] + "&" 49 | else: 50 | # path = path + key + "=" + urllib.quote(str(query[key])) + "&" 51 | path = path + key + "=" + str(query[key]) + "&" 52 | 53 | if (path.endswith("&")): 54 | path = path[:-1] 55 | 56 | return path 57 | else: 58 | return url 59 | 60 | 61 | class APIClient(object): 62 | 63 | def __init__(self, key=None, client=None, test_mode=False): 64 | self.api_key = key 65 | 66 | from openpay import verify_ssl_certs 67 | openpay.test_mode = test_mode 68 | 69 | self._client = client or http_client.new_default_http_client( 70 | verify_ssl_certs=verify_ssl_certs) 71 | 72 | def request(self, method, url, params=None): 73 | rbody, rcode, my_api_key = self.request_raw( 74 | method.lower(), url, params) 75 | resp = self.interpret_response(rbody, rcode) 76 | return resp, my_api_key 77 | 78 | def handle_api_error(self, rbody, rcode, resp): 79 | err = resp 80 | 81 | if rcode in [400, 404]: 82 | msg = err.get('description') + ", error code: " + str( 83 | resp['error_code']) 84 | raise error.InvalidRequestError( 85 | msg, err.get('request_id'), rbody, rcode, resp) 86 | elif rcode == 401: 87 | raise error.AuthenticationError( 88 | err.get('description'), rbody, rcode, resp) 89 | elif rcode == 402: 90 | raise error.CardError( 91 | err.get('description'), err.get('request_id'), 92 | err.get('error_code'), rbody, rcode, resp) 93 | else: 94 | raise error.APIError( 95 | "{0}, error code: {1}".format(err.get( 96 | 'description'), 97 | resp['error_code']), rbody, rcode, resp) 98 | 99 | def request_raw(self, method, url, params=None): 100 | """ 101 | Mechanism for issuing an API call 102 | """ 103 | from openpay import api_version 104 | 105 | if self.api_key: 106 | my_api_key = self.api_key 107 | else: 108 | from openpay import api_key 109 | my_api_key = api_key 110 | 111 | if my_api_key is None: 112 | raise error.AuthenticationError( 113 | 'No API key provided. (HINT: set your API key using ' 114 | '"openpay.api_key = "). You can generate API keys ' 115 | 'from the Openpay Dashboard. See http://docs.openpay.mx ' 116 | 'for details, or email soporte@openpay.mx if you have any ' 117 | 'questions.') 118 | 119 | abs_url = "{0}{1}".format(openpay.get_api_base(), url) 120 | 121 | if method == 'get' or method == 'delete': 122 | if params: 123 | abs_url = _build_api_url(abs_url, params) 124 | post_data = None 125 | elif method == 'post' or method == 'put': 126 | post_data = json.dumps(params) 127 | else: 128 | raise error.APIConnectionError( 129 | 'Unrecognized HTTP method %r. This may indicate a bug in the ' 130 | 'Openpay bindings. Please contact soportesu@openpay.mx for ' 131 | 'assistance.' % (method,)) 132 | 133 | ua = { 134 | 'bindings_version': version.VERSION, 135 | 'lang': 'python', 136 | 'publisher': 'openpay', 137 | 'httplib': self._client.name 138 | } 139 | for attr, func in [['lang_version', platform.python_version], 140 | ['platform', platform.platform], 141 | ['uname', lambda: ' '.join(platform.uname())]]: 142 | try: 143 | val = func() 144 | except Exception as e: 145 | val = "!! %s" % (e,) 146 | ua[attr] = val 147 | 148 | headers = { 149 | 'X-Openpay-Client-User-Agent': json.dumps(ua), 150 | 'User-Agent': 'Openpay/v1 PythonBindings/%s' % (version.VERSION,), 151 | 'content-type': 'application/json', 152 | } 153 | 154 | if api_version is not None: 155 | headers['Openpay-Version'] = api_version 156 | 157 | rbody, rcode = self._client.request( 158 | method, abs_url, headers, post_data, user=my_api_key) 159 | 160 | util.logger.info( 161 | 'API request to %s returned (response code, response body) of ' 162 | '(%d, %r)', 163 | abs_url, rcode, rbody) 164 | return rbody, rcode, my_api_key 165 | 166 | def interpret_response(self, rbody, rcode): 167 | try: 168 | if hasattr(rbody, 'decode'): 169 | rbody = rbody.decode('utf-8') 170 | 171 | if rcode == 204: 172 | rbody = json.dumps({}) 173 | 174 | resp = json.loads(rbody) 175 | except Exception: 176 | raise error.APIError( 177 | "Invalid response body from API: %s " 178 | "(HTTP response code was %d)" % (rbody, rcode), 179 | rbody, rcode) 180 | if not (200 <= rcode < 300): 181 | self.handle_api_error(rbody, rcode, resp) 182 | return resp 183 | -------------------------------------------------------------------------------- /openpay/error.py: -------------------------------------------------------------------------------- 1 | # Exceptions 2 | class OpenpayError(Exception): 3 | 4 | def __init__(self, message=None, http_body=None, http_status=None, 5 | json_body=None): 6 | super(OpenpayError, self).__init__(message) 7 | 8 | if http_body and hasattr(http_body, 'decode'): 9 | try: 10 | http_body = http_body.decode('utf=8') 11 | except: 12 | http_body = ('') 14 | 15 | self.http_body = http_body 16 | 17 | self.http_status = http_status 18 | self.json_body = json_body 19 | 20 | 21 | class APIError(OpenpayError): 22 | pass 23 | 24 | 25 | class APIConnectionError(OpenpayError): 26 | pass 27 | 28 | 29 | class CardError(OpenpayError): 30 | 31 | def __init__(self, message, param, code, http_body=None, 32 | http_status=None, json_body=None): 33 | super(CardError, self).__init__(message, 34 | http_body, http_status, json_body) 35 | 36 | self.param = param 37 | self.code = code 38 | 39 | 40 | class InvalidRequestError(OpenpayError): 41 | 42 | def __init__(self, message, param, http_body=None, 43 | http_status=None, json_body=None): 44 | super(InvalidRequestError, self).__init__( 45 | message, http_body, http_status, json_body) 46 | self.param = param 47 | 48 | 49 | class AuthenticationError(OpenpayError): 50 | pass 51 | 52 | 53 | class InvalidCountryError(OpenpayError): 54 | def __init__(self, message, param, http_body=None, http_status=None, json_body=None): 55 | super(InvalidCountryError, self).__init__(message, http_body, http_status, json_body) 56 | self.param = param 57 | -------------------------------------------------------------------------------- /openpay/http_client.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | from future.builtins import str 3 | from future.builtins import bytes 4 | 5 | import os 6 | import sys 7 | import textwrap 8 | import warnings 9 | 10 | from openpay import error 11 | 12 | # - Requests is the preferred HTTP library 13 | # - Google App Engine has urlfetch 14 | # - Use Pycurl if it's there (at least it verifies SSL certs) 15 | # - Fall back to urllib2 with a warning if needed 16 | try: 17 | import ssl, urllib2 18 | # import contextlib 19 | except ImportError: 20 | import urllib.request 21 | import urllib.error 22 | 23 | try: 24 | # base64.encodestring is deprecated in Python 3.x 25 | from base64 import encodebytes 26 | except ImportError: 27 | # Python 2.x 28 | from base64 import encodestring as encodebytes 29 | 30 | try: 31 | import pycurl 32 | except ImportError: 33 | pycurl = None 34 | 35 | try: 36 | import requests 37 | except ImportError: 38 | requests = None 39 | 40 | try: 41 | from google.appengine.api import urlfetch 42 | except ImportError: 43 | urlfetch = None 44 | 45 | 46 | def new_default_http_client(*args, **kwargs): 47 | if urlfetch: 48 | impl = UrlFetchClient 49 | elif requests: 50 | impl = RequestsClient 51 | elif pycurl: 52 | impl = PycurlClient 53 | else: 54 | impl = Urllib2Client 55 | warnings.warn( 56 | "Warning: the Openpay library is falling back to urllib2/urllib " 57 | "because neither requests nor pycurl are installed. " 58 | "urllib2's SSL implementation doesn't verify server " 59 | "certificates. For improved security, we suggest installing " 60 | "requests.") 61 | 62 | return impl(*args, **kwargs) 63 | 64 | 65 | class HTTPClient(object): 66 | 67 | def __init__(self, verify_ssl_certs=True): 68 | self._verify_ssl_certs = verify_ssl_certs 69 | 70 | def request(self, method, url, headers, post_data=None, user=None): 71 | raise NotImplementedError( 72 | 'HTTPClient subclasses must implement `request`') 73 | 74 | 75 | class RequestsClient(HTTPClient): 76 | name = 'requests' 77 | 78 | def request(self, method, url, headers, post_data=None, user=None): 79 | kwargs = {} 80 | 81 | if self._verify_ssl_certs: 82 | kwargs['verify'] = os.path.join( 83 | os.path.dirname(__file__), 'data/ca-certificates.crt') 84 | else: 85 | kwargs['verify'] = False 86 | 87 | try: 88 | try: 89 | result = requests.request(method, 90 | url, 91 | headers=headers, 92 | data=post_data, 93 | timeout=80, 94 | auth=(user, ''), 95 | ** kwargs) 96 | except TypeError as e: 97 | raise TypeError( 98 | 'Warning: It looks like your installed version of the ' 99 | '"requests" library is not compatible with Openpay\'s ' 100 | 'usage thereof. (HINT: The most likely cause is that ' 101 | 'your "requests" library is out of date. You can fix ' 102 | 'that by running "pip install -U requests".) The ' 103 | 'underlying error was: %s' % (e,)) 104 | 105 | # This causes the content to actually be read, which could cause 106 | # e.g. a socket timeout. TODO: The other fetch methods probably 107 | # are succeptible to the same and should be updated. 108 | content = result.content 109 | 110 | status_code = result.status_code 111 | except Exception as e: 112 | # Would catch just requests.exceptions.RequestException, but can 113 | # also raise ValueError, RuntimeError, etc. 114 | self._handle_request_error(e) 115 | return content, status_code 116 | 117 | def _handle_request_error(self, e): 118 | if isinstance(e, requests.exceptions.RequestException): 119 | msg = ("Unexpected error communicating with Openpay. " 120 | "If this problem persists, let us know at " 121 | "support@openpay.mx.") 122 | err = "%s: %s" % (type(e).__name__, str(e)) 123 | else: 124 | msg = ("Unexpected error communicating with Openpay. " 125 | "It looks like there's probably a configuration " 126 | "issue locally. If this problem persists, let us " 127 | "know at support@openpay.mx.") 128 | err = "A %s was raised" % (type(e).__name__,) 129 | if str(e): 130 | err += " with error message %s" % (str(e),) 131 | else: 132 | err += " with no error message" 133 | msg = textwrap.fill(msg) + "\n\n(Network error: %s)" % (err,) 134 | raise error.APIConnectionError(msg) 135 | 136 | 137 | class UrlFetchClient(HTTPClient): 138 | pass 139 | 140 | 141 | class PycurlClient(HTTPClient): 142 | pass 143 | 144 | 145 | class Urllib2Client(HTTPClient): 146 | if sys.version_info >= (3, 0): 147 | name = 'urllib.request' 148 | else: 149 | name = 'urllib2' 150 | 151 | def request(self, method, url, headers, post_data=None, user=None): 152 | if sys.version_info >= (3, 0) and isinstance(post_data, str): 153 | post_data = post_data.encode('utf-8') 154 | 155 | if sys.version_info >= (3, 0): 156 | req = urllib.request.Request(url, post_data, headers) 157 | user_string = '%s:%s' % (user, '') 158 | base64string = encodebytes(bytes(user_string, encoding='utf-8')) 159 | base64string = base64string.decode('utf-8').replace('\n', '') 160 | req.add_header("Authorization", "Basic %s" % base64string) 161 | 162 | if method not in ('get', 'post'): 163 | req.get_method = lambda: method.upper() 164 | 165 | try: 166 | with urllib.request.urlopen(req) as response: 167 | rbody = response.read() 168 | rcode = response.code 169 | except urllib.error.HTTPError as e: 170 | rcode = e.code 171 | rbody = e.read() 172 | except (urllib.error.URLError, ValueError) as e: 173 | self._handle_request_error(e) 174 | return rbody, rcode 175 | else: 176 | req = urllib2.Request(url, post_data, headers) 177 | base64string = encodebytes('%s:%s' % (user, '')) 178 | auth_string = "Basic %s" % base64string 179 | auth_string = auth_string.replace('\n', '') 180 | req.add_header("Authorization", auth_string) 181 | 182 | if method not in ('get', 'post'): 183 | req.get_method = lambda: method.upper() 184 | 185 | try: 186 | ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1_2) 187 | response = urllib2.urlopen(req, context=ctx) 188 | rbody = response.read() 189 | rcode = response.code 190 | except urllib2.HTTPError as e: 191 | rcode = e.code 192 | rbody = e.read() 193 | except (urllib2.URLError, ValueError) as e: 194 | self._handle_request_error(e) 195 | return rbody, rcode 196 | 197 | def _handle_request_error(self, e): 198 | msg = ("Unexpected error communicating with Openpay. " 199 | "If this problem persists, let us know at support@openpay.mx.") 200 | msg = textwrap.fill(msg) + "\n\n(Network error: " + str(e) + ")" 201 | raise error.APIConnectionError(msg) 202 | -------------------------------------------------------------------------------- /openpay/resource.py: -------------------------------------------------------------------------------- 1 | from __future__ import unicode_literals 2 | 3 | try: 4 | import json 5 | except ImportError: 6 | import simplejson as json 7 | 8 | try: 9 | from urllib import quote_plus 10 | except ImportError: 11 | from urllib.parse import quote_plus 12 | 13 | from future.builtins import super 14 | from future.builtins import hex 15 | from future.builtins import str 16 | import openpay 17 | from openpay import api, error 18 | from openpay.util import utf8, logger 19 | 20 | 21 | def convert_to_openpay_object(resp, api_key, item_type=None): 22 | types = {'charge': Charge, 'customer': Customer, 23 | 'plan': Plan, 'transfer': Transfer, 'list': ListObject, 24 | 'card': Card, 'payout': Payout, 'subscription': Subscription, 25 | 'bank_account': BankAccount, 'fee': Fee, 'pse': Pse, 'checkout': Checkout, 26 | 'webhook': Webhook, 'token': Token} 27 | 28 | if isinstance(resp, list): 29 | return [convert_to_openpay_object(i, api_key, item_type) for i in resp] 30 | elif isinstance(resp, dict) and not isinstance(resp, BaseObject): 31 | resp = resp.copy() 32 | klass_name = resp.get('object') 33 | if klass_name: 34 | klass_name = str(klass_name) 35 | 36 | if not klass_name and item_type: 37 | klass_name = str(item_type) 38 | if isinstance(klass_name, str): 39 | klass = types.get(klass_name, BaseObject) 40 | else: 41 | klass = BaseObject 42 | return klass.construct_from(resp, api_key) 43 | else: 44 | return resp 45 | 46 | 47 | class BaseObject(dict): 48 | 49 | def __init__(self, id=None, api_key=None, **params): 50 | super(BaseObject, self).__init__() 51 | 52 | self._unsaved_values = set() 53 | self._transient_values = set() 54 | 55 | self._retrieve_params = params 56 | self._previous_metadata = None 57 | 58 | object.__setattr__(self, 'api_key', api_key) 59 | 60 | if id: 61 | self['id'] = id 62 | 63 | def __setattr__(self, k, v): 64 | if k[0] == '_' or k in self.__dict__: 65 | return super(BaseObject, self).__setattr__(k, v) 66 | else: 67 | self[k] = v 68 | 69 | def __getattr__(self, k): 70 | if k[0] == '_': 71 | raise AttributeError(k) 72 | 73 | try: 74 | return self[k] 75 | except KeyError as err: 76 | raise AttributeError(*err.args) 77 | 78 | def __setitem__(self, k, v): 79 | if v == "": 80 | raise ValueError( 81 | "You cannot set %s to an empty string. " 82 | "We interpret empty strings as None in requests." 83 | "You may set %s.%s = None to delete the property" % 84 | (k, str(self), v)) 85 | 86 | super(BaseObject, self).__setitem__(k, v) 87 | self._unsaved_values.add(k) 88 | 89 | def __getitem__(self, k): 90 | try: 91 | return super(BaseObject, self).__getitem__(k) 92 | except KeyError as err: 93 | if k in self._transient_values: 94 | raise KeyError( 95 | "%r. HINT: The %r attribute was set in the past." 96 | "It was then wiped when refreshing the object with " 97 | "the result returned by Openpay's API, probably as a " 98 | "result of a save(). The attributes currently " 99 | "available on this object are: %s" % 100 | (k, k, ', '.join(list(self.keys())))) 101 | else: 102 | raise err 103 | 104 | def __delitem__(self, k): 105 | raise TypeError( 106 | "You cannot delete attributes on a BaseObject. " 107 | "To unset a property, set it to None.") 108 | 109 | @classmethod 110 | def construct_from(cls, values, api_key): 111 | instance = cls(values.get('id'), api_key) 112 | instance.refresh_from(values, api_key) 113 | return instance 114 | 115 | def refresh_from(self, values, api_key=None, partial=False): 116 | self.api_key = api_key or getattr(values, 'api_key', None) 117 | 118 | # Wipe old state before setting new. This is useful for e.g. 119 | # updating a customer, where there is no persistent card 120 | # parameter. Mark those values which don't persist as transient 121 | if partial: 122 | self._unsaved_values = (self._unsaved_values - set(values)) 123 | else: 124 | removed = set(self.keys()) - set(values) 125 | self._transient_values = self._transient_values | removed 126 | self._unsaved_values = set() 127 | 128 | self.clear() 129 | 130 | self._transient_values = self._transient_values - set(values) 131 | 132 | for k, v in values.items(): 133 | super(BaseObject, self).__setitem__( 134 | k, convert_to_openpay_object(v, api_key)) 135 | 136 | self._previous_metadata = values.get('metadata') 137 | 138 | def request(self, method, url, params=None): 139 | if params is None: 140 | params = self._retrieve_params 141 | 142 | requestor = api.APIClient(self.api_key) 143 | response, api_key = requestor.request(method, url, params) 144 | 145 | if isinstance(response, list): 146 | for item in response: 147 | if 'object' not in list(item.keys()): 148 | item.update({'object': self.get('item_type')}) 149 | 150 | data = { 151 | 'object': 'list', 152 | 'count': len(response), 153 | 'url': url, 154 | 'data': response, 155 | "item_type": self.get('item_type') 156 | } 157 | 158 | response = data 159 | 160 | if isinstance(response, dict) and self.get('item_type'): 161 | if 'object' not in list(response.keys()): 162 | response.update({'object': self.get('item_type')}) 163 | 164 | return convert_to_openpay_object(response, api_key) 165 | 166 | def __repr__(self): 167 | ident_parts = [type(self).__name__] 168 | 169 | if isinstance(self.get('object'), str): 170 | ident_parts.append(utf8(self.get('object'))) 171 | 172 | if isinstance(self.get('id'), str): 173 | ident_parts.append('id=%s' % utf8(self.get('id'))) 174 | 175 | return '<%s at %s> JSON: %s' % ( 176 | ' '.join(ident_parts), hex(id(self)), str(self)) 177 | 178 | def __str__(self): 179 | return json.dumps(self, sort_keys=True, indent=2) 180 | 181 | @property 182 | def openapay_id(self): 183 | return self.id 184 | 185 | 186 | class APIResource(BaseObject): 187 | 188 | @classmethod 189 | def retrieve(cls, id, api_key=None, **params): 190 | instance = cls(id, api_key, **params) 191 | instance.refresh() 192 | return instance 193 | 194 | def refresh(self): 195 | self.refresh_from(self.request('get', self.instance_url())) 196 | return self 197 | 198 | @classmethod 199 | def class_name(cls): 200 | if cls == APIResource: 201 | raise NotImplementedError( 202 | 'APIResource is an abstract class. You should perform ' 203 | 'action on its subclasses (e.g. Charge, Customer)') 204 | return str(cls.__name__.lower()) 205 | 206 | @classmethod 207 | def class_url(cls, params=None): 208 | merchant_id = openpay.merchant_id 209 | cls_name = cls.class_name() 210 | if params and 'customer' in list(params.keys()): 211 | return "/v1/{0}/customers/{1}/{2}s".format( 212 | merchant_id, params.get('customer'), cls_name) 213 | else: 214 | return "/v1/%s/%ss" % (merchant_id, cls_name) 215 | 216 | def instance_url(self): 217 | id = self.get('id') 218 | if not id: 219 | raise error.InvalidRequestError( 220 | 'Could not determine which URL to request: %s instance ' 221 | 'has invalid ID: %r' % (type(self).__name__, id), 'id') 222 | id = utf8(id) 223 | params = None 224 | if 'customer' in list(self._retrieve_params.keys()): 225 | params = {'customer': self._retrieve_params.get('customer')} 226 | 227 | base = self.class_url(params) 228 | extn = quote_plus(id) 229 | return "%s/%s" % (base, extn) 230 | 231 | 232 | class ListObject(BaseObject): 233 | 234 | def all(self, **params): 235 | return self.request('get', self['url'], params) 236 | 237 | def create(self, **params): 238 | return self.request('post', self['url'], params) 239 | 240 | def retrieve(self, id, **params): 241 | base = self.get('url') 242 | id = utf8(id) 243 | extn = quote_plus(id) 244 | url = "%s/%s" % (base, extn) 245 | 246 | return self.request('get', url, params) 247 | 248 | 249 | class SingletonAPIResource(APIResource): 250 | 251 | @classmethod 252 | def retrieve(cls, api_key=None): 253 | return super(SingletonAPIResource, cls).retrieve( 254 | None, api_key=api_key) 255 | 256 | @classmethod 257 | def class_url(cls): 258 | merchant_id = openpay.merchant_id 259 | cls_name = cls.class_name() 260 | return "/v1/{0}/{1}".format(merchant_id, cls_name) 261 | 262 | def instance_url(self): 263 | return self.class_url() 264 | 265 | 266 | # Classes of API operations 267 | class ListableAPIResource(APIResource): 268 | 269 | @classmethod 270 | def all(cls, api_key=None, **params): 271 | requestor = api.APIClient(api_key) 272 | url = cls.class_url(params) 273 | 274 | response, api_key = requestor.request('get', url, params) 275 | klass_name = cls.__name__.lower() 276 | for item in response: 277 | if 'object' not in list(item.keys()): 278 | item.update({'object': klass_name}) 279 | 280 | data = { 281 | 'object': 'list', 282 | 'count': len(response), 283 | 'url': url, 284 | 'data': response, 285 | 'item_type': klass_name, 286 | } 287 | 288 | return convert_to_openpay_object(data, api_key) 289 | 290 | 291 | class CreateableAPIResource(APIResource): 292 | 293 | @classmethod 294 | def create(cls, api_key=None, **params): 295 | requestor = api.APIClient(api_key) 296 | url = cls.class_url(params) 297 | 298 | if "clean_params" in dir(cls): 299 | params = cls.clean_params(params) 300 | 301 | response, api_key = requestor.request('post', url, params) 302 | klass_name = cls.__name__.lower() 303 | return convert_to_openpay_object(response, api_key, klass_name) 304 | 305 | 306 | class UpdateableAPIResource(APIResource): 307 | 308 | def save(self): 309 | updated_params = self.serialize(self) 310 | 311 | if getattr(self, 'metadata', None): 312 | updated_params['metadata'] = self.serialize_metadata() 313 | 314 | if updated_params: 315 | updated_params = self.copy() 316 | if ('balance' in updated_params and 317 | 'status' in updated_params): 318 | updated_params.update({'status': None, 'balance': None}) 319 | else: 320 | updated_params.update({'status': None}) 321 | 322 | self.refresh_from(self.request('put', self.instance_url(), 323 | updated_params)) 324 | else: 325 | logger.debug("Trying to save already saved object %r", self) 326 | return self 327 | 328 | def serialize_metadata(self): 329 | if 'metadata' in self._unsaved_values: 330 | # the metadata object has been reassigned 331 | # i.e. as object.metadata = {key: val} 332 | metadata_update = self.metadata 333 | keys_to_unset = set(self._previous_metadata.keys()) - \ 334 | set(self.metadata.keys()) 335 | for key in keys_to_unset: 336 | metadata_update[key] = "" 337 | 338 | return metadata_update 339 | else: 340 | return self.serialize(self.metadata) 341 | 342 | def serialize(self, obj): 343 | params = {} 344 | if obj._unsaved_values: 345 | for k in obj._unsaved_values: 346 | if k == 'id' or k == '_previous_metadata': 347 | continue 348 | v = getattr(obj, k) 349 | params[k] = v if v is not None else "" 350 | return params 351 | 352 | 353 | class DeletableAPIResource(APIResource): 354 | 355 | def delete(self, **params): 356 | self.refresh_from(self.request('delete', self.instance_url(), params)) 357 | return self 358 | 359 | 360 | # API objects 361 | 362 | 363 | class Card(ListableAPIResource, UpdateableAPIResource, 364 | DeletableAPIResource, CreateableAPIResource): 365 | 366 | @classmethod 367 | def class_url(cls, params=None): 368 | merchant_id = openpay.merchant_id 369 | cls_name = cls.class_name() 370 | if params and 'customer' in list(params.keys()): 371 | return "/v1/{0}/customers/{1}/{2}s".format( 372 | merchant_id, params.get('customer'), cls_name) 373 | else: 374 | return "/v1/%s/%ss" % (merchant_id, cls_name) 375 | 376 | def instance_url(self): 377 | self.id = utf8(self.id) 378 | self.customer = utf8(getattr(self, 'customer', self.customer_id)) 379 | extn = quote_plus(self.id) 380 | return "%s/%s/cards/%s" % (Customer.class_url(), self.customer, extn) 381 | 382 | @classmethod 383 | def retrieve(cls, id, api_key=None, **params): 384 | raise NotImplementedError( 385 | "Can't retrieve a card without a customer ID. Use " 386 | "customer.cards.retrieve('card_id') instead.") 387 | 388 | def save(self): 389 | raise NotImplementedError("This feature is not supported yet by API") 390 | 391 | 392 | class Charge(CreateableAPIResource, ListableAPIResource, 393 | UpdateableAPIResource): 394 | 395 | @classmethod 396 | def clean_params(cls, params=None): 397 | if params and params.get('customer', None) is not None: 398 | del params['customer'] 399 | 400 | return params 401 | 402 | @classmethod 403 | def class_url(cls, params=None): 404 | merchant_id = openpay.merchant_id 405 | cls_name = cls.class_name() 406 | if params and 'customer' in params: 407 | return "/v1/{0}/customers/{1}/{2}s".format( 408 | merchant_id, params['customer'], cls_name) 409 | else: 410 | return "/v1/%s/%ss" % (merchant_id, cls_name) 411 | 412 | def instance_url(self): 413 | self.id = utf8(self.id) 414 | 415 | if getattr(self, '_as_merchant', False): 416 | extn = quote_plus(self.id) 417 | url = "{0}/{1}".format(Charge.class_url(), extn) 418 | else: 419 | self.customer = utf8(self.customer_id) 420 | extn = quote_plus(self.id) 421 | url = "%s/%s/charges/%s" % ( 422 | Customer.class_url(), self.customer, extn) 423 | 424 | return url 425 | 426 | def refund(self, **params): 427 | self._as_merchant = params.pop('merchant', False) 428 | url = self.instance_url() + '/refund' 429 | self.refresh_from(self.request('post', url, params)) 430 | return self 431 | 432 | def capture(self, **params): 433 | self._as_merchant = params.pop('merchant', False) 434 | url = self.instance_url() + '/capture' 435 | self.refresh_from(self.request('post', url, params)) 436 | return self 437 | 438 | def update_dispute(self, **params): 439 | requestor = api.APIClient(self.api_key) 440 | url = self.instance_url() + '/dispute' 441 | response, api_key = requestor.request('post', url, params) 442 | self.refresh_from({'dispute': response}, api_key, True) 443 | return self.dispute 444 | 445 | def close_dispute(self): 446 | requestor = api.APIClient(self.api_key) 447 | url = self.instance_url() + '/dispute/close' 448 | response, api_key = requestor.request('post', url, {}) 449 | self.refresh_from({'dispute': response}, api_key, True) 450 | return self.dispute 451 | 452 | @classmethod 453 | def as_merchant(cls): 454 | 455 | params = {} 456 | if hasattr(cls, 'api_key'): 457 | api_key = cls.api_key 458 | else: 459 | api_key = openpay.api_key 460 | 461 | requestor = api.APIClient(api_key) 462 | url = cls.class_url() 463 | response, api_key = requestor.request('get', url, params) 464 | return convert_to_openpay_object(response, api_key, 'charge') 465 | 466 | @classmethod 467 | def retrieve_as_merchant(cls, id): 468 | params = {} 469 | api_key = getattr(cls, 'api_key', openpay.api_key) 470 | cls._as_merchant = True 471 | requestor = api.APIClient(api_key) 472 | uid = utf8(id) 473 | url = "%s/%s" % (cls.class_url(), quote_plus(uid)) 474 | response, api_key = requestor.request('get', url, params) 475 | return convert_to_openpay_object(response, api_key, 'charge') 476 | 477 | @classmethod 478 | def create_as_merchant(cls, **params): 479 | api_key = getattr(cls, 'api_key', openpay.api_key) 480 | requestor = api.APIClient(api_key) 481 | # charge over merchant 482 | response, api_key = requestor.request('post', cls.class_url(), params) 483 | return convert_to_openpay_object(response, api_key, 'charge') 484 | 485 | 486 | class Customer(CreateableAPIResource, UpdateableAPIResource, 487 | ListableAPIResource, DeletableAPIResource): 488 | 489 | @property 490 | def cards(self): 491 | data = { 492 | 'object': 'list', 493 | 'url': Card.class_url({'customer': self.id}), 494 | 'count': 0, 495 | 'item_type': 'card' 496 | } 497 | 498 | if not hasattr(self, '_cards'): 499 | self._cards = convert_to_openpay_object(data, self.api_key) 500 | 501 | return self._cards 502 | 503 | @property 504 | def charges(self): 505 | data = { 506 | 'object': 'list', 507 | 'url': Charge.class_url({'customer': self.id}), 508 | 'count': 0, 509 | 'item_type': 'charge' 510 | } 511 | 512 | if not hasattr(self, '_charges'): 513 | self._charges = convert_to_openpay_object(data, self.api_key) 514 | 515 | return self._charges 516 | 517 | @property 518 | def transfers(self): 519 | data = { 520 | 'object': 'list', 521 | 'url': Transfer.class_url({'customer': self.id}), 522 | 'count': 0, 523 | 'item_type': 'transfer' 524 | } 525 | 526 | if not hasattr(self, '_transfers'): 527 | self._transfers = convert_to_openpay_object(data, self.api_key) 528 | 529 | return self._transfers 530 | 531 | @property 532 | def payouts(self): 533 | data = { 534 | 'object': 'list', 535 | 'url': Payout.class_url({'customer': self.id}), 536 | 'count': 0, 537 | 'item_type': 'payout' 538 | } 539 | 540 | if not hasattr(self, '_payouts'): 541 | self._payouts = convert_to_openpay_object(data, self.api_key) 542 | 543 | return self._payouts 544 | 545 | @property 546 | def bank_accounts(self): 547 | data = { 548 | 'object': 'list', 549 | 'url': BankAccount.class_url({'customer': self.id}), 550 | 'count': 0, 551 | 'item_type': 'bank_account' 552 | } 553 | 554 | if not hasattr(self, '_back_accounts'): 555 | self._back_accounts = convert_to_openpay_object(data, self.api_key) 556 | 557 | return self._back_accounts 558 | 559 | @property 560 | def subscriptions(self): 561 | data = { 562 | 'object': 'list', 563 | 'url': Subscription.class_url({'customer': self.id}), 564 | 'count': 0, 565 | 'item_type': 'subscription' 566 | } 567 | 568 | if not hasattr(self, '_subscriptions'): 569 | self._subscriptions = convert_to_openpay_object(data, self.api_key) 570 | 571 | return self._subscriptions 572 | 573 | @property 574 | def pse(self): 575 | data = { 576 | 'object': 'list', 577 | 'count': 0, 578 | 'url':Pse.build_url(self.id), 579 | 'item_type': 'pse' 580 | } 581 | if not hasattr(self, '_pse'): 582 | self._pse = convert_to_openpay_object(data, self.api_key) 583 | return self._pse 584 | 585 | @property 586 | def checkouts(self): 587 | data = { 588 | 'object': 'list', 589 | 'count': 0, 590 | 'url':Checkout.build_url(self.id), 591 | 'item_type': 'checkout' 592 | } 593 | if not hasattr(self, '_checkouts'): 594 | self._checkouts = convert_to_openpay_object(data, self.api_key) 595 | return self._checkouts 596 | 597 | 598 | class Plan(CreateableAPIResource, DeletableAPIResource, 599 | UpdateableAPIResource, ListableAPIResource): 600 | pass 601 | 602 | 603 | class Transfer(CreateableAPIResource, UpdateableAPIResource, 604 | ListableAPIResource): 605 | pass 606 | 607 | 608 | class BankAccount(CreateableAPIResource, UpdateableAPIResource, 609 | DeletableAPIResource, ListableAPIResource): 610 | def instance_url(self): 611 | self.id = utf8(self.id) 612 | if hasattr(self, 'customer'): 613 | self.customer = utf8(self.customer) 614 | else: 615 | self.customer = utf8(self.customer_id) 616 | 617 | base = Customer.class_url() 618 | cust_extn = self.customer 619 | extn = quote_plus(self.id) 620 | 621 | return "%s/%s/bankaccounts/%s" % (base, cust_extn, extn) 622 | 623 | 624 | class Payout(CreateableAPIResource, ListableAPIResource, 625 | DeletableAPIResource): 626 | 627 | @classmethod 628 | def create_as_merchant(cls, **params): 629 | api_key = getattr(cls, 'api_key', openpay.api_key) 630 | requestor = api.APIClient(api_key) 631 | response, api_key = requestor.request('post', cls.class_url(), params) 632 | return convert_to_openpay_object(response, api_key, 'payout') 633 | 634 | @classmethod 635 | def retrieve_as_merchant(cls, payout_id): 636 | params = {} 637 | api_key = getattr(cls, 'api_key', openpay.api_key) 638 | 639 | requestor = api.APIClient(api_key) 640 | url = "{0}/{1}".format(cls.class_url(), payout_id) 641 | response, api_key = requestor.request('get', url, params) 642 | return convert_to_openpay_object(response, api_key, 'payout') 643 | 644 | 645 | class Fee(CreateableAPIResource, ListableAPIResource): 646 | 647 | @classmethod 648 | def refund(cls, fee_id, **params): 649 | if hasattr(cls, 'api_key'): 650 | api_key = cls.api_key 651 | else: 652 | api_key = openpay.api_key 653 | 654 | requestor = api.APIClient(api_key) 655 | url = cls.class_url() 656 | url = "{0}/{1}/refund".format(url, fee_id) 657 | response, api_key = requestor.request('post', url, params) 658 | return convert_to_openpay_object(response, api_key, 'fee') 659 | 660 | 661 | class Subscription(DeletableAPIResource, UpdateableAPIResource): 662 | 663 | def instance_url(self): 664 | self.id = utf8(self.id) 665 | self.customer = utf8(getattr(self, 'customer', self.customer_id)) 666 | extn = quote_plus(self.id) 667 | 668 | return "%s/%s/subscriptions/%s" % (Customer.class_url(), 669 | self.customer, extn) 670 | 671 | 672 | class Pse(CreateableAPIResource): 673 | 674 | @classmethod 675 | def create(cls, customer_id=None, **params): 676 | if hasattr(cls, 'api_key'): 677 | api_key = cls.api_key 678 | else: 679 | api_key = openpay.api_key 680 | requestor = api.APIClient(api_key) 681 | url = cls.build_url(customer_id) 682 | response, api_key = requestor.request('post', url, params) 683 | openpay_object = convert_to_openpay_object(response, api_key) 684 | return openpay_object 685 | 686 | @classmethod 687 | def build_url(cls, customer_id=None): 688 | merchant_id = openpay.merchant_id 689 | if customer_id == None: 690 | return "/v1/{0}/charges".format(merchant_id, customer_id) 691 | else: 692 | return "/v1/{0}/customers/{1}/charges".format(merchant_id, customer_id) 693 | 694 | 695 | class Webhook(CreateableAPIResource, ListableAPIResource, DeletableAPIResource): 696 | @classmethod 697 | def retrieve(cls, webhook_id=None, api_key=None, **params): 698 | api_key = getattr(cls, 'api_key', openpay.api_key) 699 | requestor = api.APIClient(api_key) 700 | url = cls.build_url(webhook_id) 701 | response, api_key = requestor.request('get', url, params) 702 | return convert_to_openpay_object(response, api_key, 'checkout') 703 | 704 | @classmethod 705 | def build_url(cls, webhook_id): 706 | merchant_id = openpay.merchant_id 707 | if webhook_id is None: 708 | return "/v1/{0}/webhooks".format(merchant_id) 709 | if webhook_id is not None: 710 | return "/v1/{0}/webhooks/{1}".format(merchant_id, webhook_id) 711 | 712 | class Checkout(CreateableAPIResource, 713 | UpdateableAPIResource, ListableAPIResource): 714 | 715 | @classmethod 716 | def create(cls, customer_id=None, **params): 717 | if hasattr(cls, 'api_key'): 718 | api_key = cls.api_key 719 | else: 720 | api_key = openpay.api_key 721 | requestor = api.APIClient(api_key) 722 | url = cls.build_url(customer=customer_id) 723 | response, api_key = requestor.request('post', url, params) 724 | openpay_object = convert_to_openpay_object(response, api_key) 725 | return openpay_object 726 | 727 | @classmethod 728 | def retrieve(cls, api_key=None, checkout_id=None, **params): 729 | api_key = getattr(cls, 'api_key', openpay.api_key) 730 | requestor = api.APIClient(api_key) 731 | url = cls.build_url(checkout_id) 732 | response, api_key = requestor.request('get', url, params) 733 | return convert_to_openpay_object(response, api_key, 'checkout') 734 | 735 | @classmethod 736 | def build_url(cls, checkout_id=None, customer=None): 737 | merchant_id = openpay.merchant_id 738 | if checkout_id is None and customer is None: 739 | return "/v1/{0}/checkouts".format(merchant_id) 740 | if customer is not None: 741 | return "/v1/{0}/customers/{1}/checkouts".format(merchant_id, customer) 742 | if checkout_id is not None: 743 | return "/v1/{0}/checkouts/{1}".format(merchant_id, checkout_id) 744 | 745 | def save(self): 746 | updated_params = self.serialize(self) 747 | 748 | if getattr(self, 'metadata', None): 749 | updated_params['metadata'] = self.serialize_metadata() 750 | 751 | if updated_params: 752 | self.refresh_from(self.request('put', self.instance_url(), 753 | updated_params)) 754 | else: 755 | logger.debug("Trying to save already saved object %r", self) 756 | return self 757 | 758 | def serialize_metadata(self): 759 | if 'metadata' in self._unsaved_values: 760 | # the metadata object has been reassigned 761 | # i.e. as object.metadata = {key: val} 762 | metadata_update = self.metadata 763 | keys_to_unset = set(self._previous_metadata.keys()) - \ 764 | set(self.metadata.keys()) 765 | for key in keys_to_unset: 766 | metadata_update[key] = "" 767 | 768 | return metadata_update 769 | else: 770 | return self.serialize(self.metadata) 771 | 772 | def serialize(self, obj): 773 | params = {} 774 | if obj._unsaved_values: 775 | for k in obj._unsaved_values: 776 | if k == 'id' or k == '_previous_metadata': 777 | continue 778 | v = getattr(obj, k) 779 | params[k] = v if v is not None else "" 780 | return params 781 | 782 | def instance_url(self): 783 | id = self.get('id') 784 | status = self.get('status') 785 | if not id: 786 | raise error.InvalidRequestError( 787 | 'Could not determine which URL to request: %s instance ' 788 | 'has invalid ID: %r' % (type(self).__name__, id), 'id') 789 | id = utf8(id) 790 | base = self.class_url() 791 | extn = quote_plus(id) 792 | return "%s/%s?status=%s" % (base, extn, status) 793 | 794 | class Token(CreateableAPIResource): 795 | pass 796 | -------------------------------------------------------------------------------- /openpay/test/__init__.py: -------------------------------------------------------------------------------- 1 | import pkgutil 2 | import unittest 3 | 4 | 5 | def all_names(): 6 | for _, modname, _ in pkgutil.iter_modules(__path__): 7 | if modname.startswith('test_'): 8 | yield 'openpay.test.' + modname 9 | 10 | 11 | def all(): 12 | return unittest.defaultTestLoader.loadTestsFromNames(all_names()) 13 | 14 | 15 | def unit(): 16 | unit_names = [name for name in all_names() if 'integration' not in name] 17 | return unittest.defaultTestLoader.loadTestsFromNames(unit_names) 18 | -------------------------------------------------------------------------------- /openpay/test/helper.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import datetime 5 | import os 6 | import random 7 | import re 8 | import string 9 | import sys 10 | import time 11 | import unittest 12 | 13 | from future.builtins import range 14 | from future.builtins import str 15 | from future.builtins import super 16 | from mock import patch, Mock 17 | 18 | import openpay 19 | 20 | 21 | def generate_order_id(): 22 | order_id = 'oid-test-{0}-{1}'.format( 23 | random.randint(1, 3000), str(time.time())[7:]) 24 | if len(order_id) > 20: 25 | order_id = order_id[:20] 26 | 27 | return order_id 28 | 29 | 30 | NOW = datetime.datetime.now() 31 | 32 | DUMMY_CARD = { 33 | 'card_number': '4111111111111111', 34 | 'holder_name': 'Juan Lopez', 35 | 'expiration_month': NOW.month, 36 | 'expiration_year': str(NOW.year + 4)[2:], 37 | "cvv2": "110", 38 | "address": { 39 | "line1": "Av. 5 de febrero No. 1080 int Roble 207", 40 | "line2": "Carrillo puerto", 41 | "line3": "Zona industrial carrillo puerto", 42 | "postal_code": "06500", 43 | "state": "Querétaro", 44 | "city": "Querétaro", 45 | "country_code": "MX" 46 | } 47 | } 48 | 49 | DUMMY_CHARGE = { 50 | 'amount': 100, 51 | 'card': DUMMY_CARD, 52 | 'order_id': generate_order_id(), 53 | 'method': 'card', 54 | 'description': 'Dummy Charge', 55 | } 56 | 57 | DUMMY_CHARGE_STORE = { 58 | 'amount': 100, 59 | 'method': 'store', 60 | 'description': 'Dummy Charge on Store', 61 | } 62 | 63 | DUMMY_PLAN = { 64 | 'amount': 2000, 65 | 'status_after_retry': 'cancelled', 66 | 'name': 'Amazing Gold Plan', 67 | 'retry_times': 2, 68 | 'repeat_unit': 'month', 69 | 'trial_days': 0, 70 | 'repeat_every': 1, 71 | 'id': ('openpay-test-gold-' + 72 | ''.join(random.choice(string.ascii_lowercase) for x in range(10))) 73 | } 74 | 75 | DUMMY_TRANSFER = { 76 | 'amount': 400, 77 | 'customer_id': 'acuqxruyv0hi1wfdwmym', 78 | 'description': 'Dummy Transfer', 79 | 'order_id': 'oid-00099', 80 | } 81 | 82 | 83 | 84 | class OpenpayTestCase(unittest.TestCase): 85 | RESTORE_ATTRIBUTES = ('api_version', 'api_key') 86 | 87 | def setUp(self): 88 | super(OpenpayTestCase, self).setUp() 89 | 90 | self._openpay_original_attributes = {} 91 | 92 | for attr in self.RESTORE_ATTRIBUTES: 93 | self._openpay_original_attributes[attr] = getattr(openpay, attr) 94 | 95 | api_base = os.environ.get('OPENPAY_API_BASE') 96 | if api_base: 97 | openpay.api_base = api_base 98 | # Sandbox 99 | openpay.api_key = os.environ.get( 100 | 'OPENPAY_API_KEY', 'sk_10d37cc4da8e4ffd902cdf62e37abd1b') 101 | openpay.merchant_id = "mynvbjhtzxdyfewlzmdo" 102 | openpay.country = "mx" 103 | # Dev 104 | # openpay.api_key = os.environ.get( 105 | # 'OPENPAY_API_KEY', '68df281c16184d47bb773d70abd4191b') 106 | # openpay.merchant_id = "m4se8bd4fef1mkzk6d1b" 107 | openpay.verify_ssl_certs = False 108 | 109 | def tearDown(self): 110 | super(OpenpayTestCase, self).tearDown() 111 | 112 | for attr in self.RESTORE_ATTRIBUTES: 113 | setattr(openpay, attr, self._openpay_original_attributes[attr]) 114 | 115 | # Python < 2.7 compatibility 116 | def assertRaisesRegexp(self, exception, regexp, callable, *args, **kwargs): 117 | try: 118 | callable(*args, **kwargs) 119 | except exception as err: 120 | if regexp is None: 121 | return True 122 | 123 | if isinstance(regexp, str): 124 | regexp = re.compile(regexp) 125 | if not regexp.search(str(err)): 126 | raise self.failureException('"%s" does not match "%s"' % 127 | (regexp.pattern, str(err))) 128 | else: 129 | raise self.failureException( 130 | '%s was not raised' % (exception.__name__,)) 131 | 132 | 133 | class OpenpayUnitTestCase(OpenpayTestCase): 134 | REQUEST_LIBRARIES = ['urlfetch', 'requests', 'pycurl'] 135 | 136 | if sys.version_info >= (3, 0): 137 | REQUEST_LIBRARIES.append('urllib.request') 138 | else: 139 | REQUEST_LIBRARIES.append('urllib2') 140 | 141 | def setUp(self): 142 | super(OpenpayUnitTestCase, self).setUp() 143 | 144 | self.request_patchers = {} 145 | self.request_mocks = {} 146 | for lib in self.REQUEST_LIBRARIES: 147 | patcher = patch("openpay.http_client.%s" % (lib,)) 148 | 149 | self.request_mocks[lib] = patcher.start() 150 | self.request_patchers[lib] = patcher 151 | 152 | def tearDown(self): 153 | super(OpenpayUnitTestCase, self).tearDown() 154 | 155 | for patcher in list(self.request_patchers.values()): 156 | patcher.stop() 157 | 158 | 159 | class OpenpayApiTestCase(OpenpayTestCase): 160 | 161 | def setUp(self): 162 | super(OpenpayApiTestCase, self).setUp() 163 | 164 | self.requestor_patcher = patch('openpay.api.APIClient') 165 | requestor_class_mock = self.requestor_patcher.start() 166 | self.requestor_mock = requestor_class_mock.return_value 167 | 168 | def tearDown(self): 169 | super(OpenpayApiTestCase, self).tearDown() 170 | 171 | self.requestor_patcher.stop() 172 | 173 | def mock_response(self, res): 174 | self.requestor_mock.request = Mock(return_value=(res, 'reskey')) 175 | 176 | 177 | class MyResource(openpay.resource.APIResource): 178 | pass 179 | 180 | 181 | class MySingleton(openpay.resource.SingletonAPIResource): 182 | pass 183 | 184 | 185 | class MyListable(openpay.resource.ListableAPIResource): 186 | pass 187 | 188 | 189 | class MyCreatable(openpay.resource.CreateableAPIResource): 190 | pass 191 | 192 | 193 | class MyUpdateable(openpay.resource.UpdateableAPIResource): 194 | pass 195 | 196 | 197 | class MyDeletable(openpay.resource.DeletableAPIResource): 198 | pass 199 | 200 | 201 | class MyComposite(openpay.resource.ListableAPIResource, 202 | openpay.resource.CreateableAPIResource, 203 | openpay.resource.UpdateableAPIResource, 204 | openpay.resource.DeletableAPIResource): 205 | pass 206 | -------------------------------------------------------------------------------- /openpay/test/test_integration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | from future.builtins import zip 4 | from future.builtins import int 5 | from future.builtins import str 6 | from future.builtins import super 7 | 8 | import datetime 9 | import os 10 | import sys 11 | import time 12 | import unittest 13 | 14 | from mock import patch 15 | 16 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) 17 | import openpay 18 | 19 | from openpay.test.helper import ( 20 | OpenpayTestCase, 21 | NOW, DUMMY_CARD, DUMMY_CHARGE, DUMMY_PLAN, 22 | DUMMY_CHARGE_STORE, generate_order_id) 23 | 24 | 25 | class FunctionalTests(OpenpayTestCase): 26 | request_client = openpay.http_client.Urllib2Client 27 | 28 | def setUp(self): 29 | super(FunctionalTests, self).setUp() 30 | 31 | def get_http_client(*args, **kwargs): 32 | return self.request_client(*args, **kwargs) 33 | 34 | self.client_patcher = patch( 35 | 'openpay.http_client.new_default_http_client') 36 | 37 | client_mock = self.client_patcher.start() 38 | client_mock.side_effect = get_http_client 39 | 40 | def tearDown(self): 41 | super(FunctionalTests, self).tearDown() 42 | 43 | self.client_patcher.stop() 44 | 45 | def test_dns_failure(self): 46 | self.patched_api_base = patch( 47 | 'openpay.get_api_base', 48 | lambda: 'https://my-invalid-domain.ireallywontresolve/v1') 49 | # get_api_base_mock = self.patched_api_base.start() 50 | self.patched_api_base.start() 51 | try: 52 | self.assertRaises(openpay.error.APIConnectionError, 53 | openpay.Customer.create) 54 | finally: 55 | self.patched_api_base.stop() 56 | 57 | def test_run(self): 58 | DUMMY_CHARGE['order_id'] = generate_order_id() 59 | charge = openpay.Charge.create(**DUMMY_CHARGE) 60 | # self.assertFalse(hasattr(charge, 'refund')) 61 | charge.refund(merchant=True) 62 | self.assertTrue(hasattr(charge, 'refund')) 63 | 64 | def test_refresh(self): 65 | DUMMY_CHARGE['order_id'] = generate_order_id() 66 | charge = openpay.Charge.create(**DUMMY_CHARGE) 67 | charge2 = openpay.Charge.retrieve_as_merchant(charge.id) 68 | self.assertEqual(charge2.creation_date, charge.creation_date) 69 | 70 | charge2.junk = 'junk' 71 | charge2.refresh() 72 | self.assertRaises(AttributeError, lambda: charge2.junk) 73 | 74 | def test_list_accessors(self): 75 | customer = openpay.Customer.create( 76 | name="Miguel Lopez", email="mlopez@example.com") 77 | self.assertEqual(customer['creation_date'], customer.creation_date) 78 | customer['foo'] = 'bar' 79 | self.assertEqual(customer.foo, 'bar') 80 | 81 | def test_raise(self): 82 | EXPIRED_CARD = DUMMY_CARD.copy() 83 | expiration_year = NOW.year - 2 84 | EXPIRED_CARD['expiration_month'] = NOW.month 85 | EXPIRED_CARD['expiration_year'] = str(expiration_year)[2:] 86 | self.assertRaises(openpay.error.InvalidRequestError, openpay.Charge.create, 87 | amount=100, method='card', description="Test Order", 88 | order_id="oid-00080", card=EXPIRED_CARD) 89 | 90 | def test_unicode(self): 91 | # Make sure unicode requests can be sent 92 | self.assertRaises(openpay.error.InvalidRequestError, 93 | openpay.Charge.retrieve_as_merchant, 94 | id=u'☃') 95 | 96 | # def test_none_values(self): 97 | # customer = openpay.Customer.create(name=None, last_name=None) 98 | # self.assertTrue(customer.id) 99 | 100 | def test_missing_id(self): 101 | customer = openpay.Customer() 102 | self.assertRaises(openpay.error.InvalidRequestError, customer.refresh) 103 | 104 | 105 | class RequestsFunctionalTests(FunctionalTests): 106 | request_client = openpay.http_client.RequestsClient 107 | 108 | # Avoid skipTest errors in < 2.7 109 | if sys.version_info >= (2, 7): 110 | class UrlfetchFunctionalTests(FunctionalTests): 111 | request_client = 'urlfetch' 112 | 113 | def setUp(self): 114 | if openpay.http_client.urlfetch is None: 115 | self.skipTest( 116 | '`urlfetch` from Google App Engine is unavailable.') 117 | else: 118 | super(UrlfetchFunctionalTests, self).setUp() 119 | 120 | 121 | # class PycurlFunctionalTests(FunctionalTests): 122 | # def setUp(self): 123 | # if sys.version_info >= (3, 0): 124 | # self.skipTest('Pycurl is not supported in Python 3') 125 | # else: 126 | # super(PycurlFunctionalTests, self).setUp() 127 | 128 | # request_client = openpay.http_client.PycurlClient 129 | 130 | 131 | class AuthenticationErrorTest(OpenpayTestCase): 132 | 133 | def test_invalid_credentials(self): 134 | key = openpay.api_key 135 | try: 136 | openpay.api_key = 'invalid' 137 | openpay.Customer.create() 138 | except openpay.error.AuthenticationError as e: 139 | self.assertEqual(401, e.http_status) 140 | self.assertTrue(isinstance(e.http_body, str)) 141 | self.assertTrue(isinstance(e.json_body, dict)) 142 | finally: 143 | openpay.api_key = key 144 | 145 | 146 | class CardErrorTest(OpenpayTestCase): 147 | 148 | def test_declined_card_props(self): 149 | EXPIRED_CARD = DUMMY_CARD.copy() 150 | expiration_year = NOW.year - 2 151 | EXPIRED_CARD['expiration_month'] = NOW.month 152 | EXPIRED_CARD['expiration_year'] = str(expiration_year)[2:] 153 | try: 154 | openpay.Charge.create(amount=100, method='card', 155 | description="Test Order", 156 | order_id="oid-00080", 157 | card=EXPIRED_CARD) 158 | except openpay.error.InvalidRequestError as e: 159 | self.assertEqual(400, e.http_status) 160 | self.assertTrue(isinstance(e.http_body, str)) 161 | self.assertTrue(isinstance(e.json_body, dict)) 162 | 163 | # Note that these are in addition to the core functional charge tests 164 | 165 | class ChargeTest(OpenpayTestCase): 166 | 167 | def setUp(self): 168 | super(ChargeTest, self).setUp() 169 | 170 | def test_charge_list_all(self): 171 | charge_list = openpay.Charge.all( 172 | creation={'lte': NOW.strftime("%Y-%m-%d")}) 173 | list_result = charge_list.all( 174 | creation={'lte': NOW.strftime("%Y-%m-%d")}) 175 | 176 | self.assertEqual(len(charge_list.data), 177 | len(list_result.data)) 178 | 179 | for expected, actual in zip(charge_list.data, 180 | list_result.data): 181 | self.assertEqual(expected.id, actual.id) 182 | 183 | def test_charge_list_create(self): 184 | charge_list = openpay.Charge.all() 185 | DUMMY_CHARGE['order_id'] = generate_order_id() 186 | charge = charge_list.create(**DUMMY_CHARGE) 187 | 188 | self.assertTrue(isinstance(charge, openpay.Charge)) 189 | self.assertEqual(DUMMY_CHARGE['amount'], charge.amount) 190 | 191 | def test_charge_list_retrieve(self): 192 | charge_list = openpay.Charge.all() 193 | charge = charge_list.retrieve(charge_list.data[0].id) 194 | self.assertTrue(isinstance(charge, openpay.Charge)) 195 | 196 | def test_charge_capture(self): 197 | params = DUMMY_CHARGE.copy() 198 | params['capture'] = False 199 | 200 | charge = openpay.Charge.create(**params) 201 | 202 | self.assertFalse(hasattr(charge, 'captured')) 203 | 204 | self.assertTrue(charge is charge.capture(merchant=True)) 205 | self.assertEqual( 206 | openpay.Charge.retrieve_as_merchant(charge.id).status, 207 | 'completed') 208 | 209 | def test_charge_store_as_customer(self): 210 | customer = openpay.Customer.create( 211 | name="Miguel Lopez", email="mlopez@example.com") 212 | charge = customer.charges.create(**DUMMY_CHARGE_STORE) 213 | self.assertTrue(hasattr(charge, 'payment_method')) 214 | self.assertTrue(hasattr(charge.payment_method, 'reference')) 215 | self.assertTrue(hasattr(charge.payment_method, 'barcode_url')) 216 | self.assertEqual( 217 | customer.charges.retrieve(charge.id).status, 218 | 'in_progress') 219 | 220 | def test_charge_store_as_merchant(self): 221 | charge = openpay.Charge.create(**DUMMY_CHARGE_STORE) 222 | 223 | self.assertTrue(hasattr(charge, 'payment_method')) 224 | self.assertTrue(hasattr(charge.payment_method, 'reference')) 225 | self.assertTrue(hasattr(charge.payment_method, 'barcode_url')) 226 | self.assertEqual(charge.payment_method.type, "store") 227 | self.assertTrue(isinstance(charge, openpay.Charge)) 228 | self.assertEqual( 229 | openpay.Charge.retrieve_as_merchant(charge.id).status, 230 | 'in_progress') 231 | 232 | 233 | class CustomerTest(OpenpayTestCase): 234 | 235 | def test_list_customers(self): 236 | customers = openpay.Customer.all() 237 | self.assertTrue(isinstance(customers.data, list)) 238 | 239 | def test_list_charges(self): 240 | customer = openpay.Customer.create( 241 | name="Miguel Lopez", 242 | email="mlopez@example.com", 243 | description="foo bar") 244 | 245 | customer.charges.create( 246 | amount=100, method="card", 247 | description="Customer test charge", 248 | order_id=generate_order_id(), card=DUMMY_CARD) 249 | 250 | self.assertEqual(1, 251 | len(customer.charges.all().data)) 252 | 253 | # def test_unset_description(self): 254 | # customer = openpay.Customer.create( 255 | # name="Miguel", last_name="Lopez", 256 | # email="mlopez@example.com", description="foo bar") 257 | # 258 | # customer.description = None 259 | # customer.save() 260 | # 261 | # self.assertEqual( 262 | # None, 263 | # customer.retrieve(customer.id).get('description')) 264 | # 265 | # def test_cannot_set_empty_string(self): 266 | # customer = openpay.Customer() 267 | # self.assertRaises(ValueError, setattr, customer, "description", "") 268 | 269 | # def test_update_customer_card(self): 270 | # customer = openpay.Customer.all(limit=1).data[0] 271 | # card = customer.cards.create(**DUMMY_CARD) 272 | # print card 273 | # card.name = 'Python bindings test' 274 | # card.save() 275 | 276 | # self.assertEqual('Python bindings test', 277 | # customer.cards.retrieve(card.id).name) 278 | 279 | 280 | class CustomerPlanTest(OpenpayTestCase): 281 | 282 | def setUp(self): 283 | super(CustomerPlanTest, self).setUp() 284 | try: 285 | self.plan_obj = openpay.Plan.create(**DUMMY_PLAN) 286 | except openpay.error.InvalidRequestError: 287 | self.plan_obj = None 288 | 289 | def tearDown(self): 290 | if self.plan_obj: 291 | try: 292 | self.plan_obj.delete() 293 | except openpay.error.InvalidRequestError: 294 | pass 295 | super(CustomerPlanTest, self).tearDown() 296 | 297 | def test_create_customer(self): 298 | self.assertRaises(openpay.error.InvalidRequestError, 299 | openpay.Customer.create, 300 | plan=DUMMY_PLAN['id']) 301 | customer = openpay.Customer.create( 302 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 303 | 304 | subscription = customer.subscriptions.create( 305 | plan_id=self.plan_obj.id, trial_days="0", card=DUMMY_CARD) 306 | 307 | self.assertTrue(isinstance(subscription, openpay.Subscription)) 308 | subscription.delete() 309 | customer.delete() 310 | self.assertFalse(hasattr(customer, 'subscription')) 311 | self.assertFalse(hasattr(customer, 'plan')) 312 | 313 | def test_update_and_cancel_subscription(self): 314 | customer = openpay.Customer.create( 315 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 316 | 317 | sub = customer.subscriptions.create( 318 | plan_id=self.plan_obj.id, card=DUMMY_CARD) 319 | 320 | sub.cancel_at_period_end = True 321 | sub.save() 322 | self.assertEqual(sub.status, 'active') 323 | self.assertTrue(sub.cancel_at_period_end) 324 | sub.delete() 325 | 326 | def test_datetime_trial_end(self): 327 | trial_end = datetime.datetime.now() + datetime.timedelta(days=15) 328 | customer = openpay.Customer.create( 329 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 330 | subscription = customer.subscriptions.create( 331 | plan_id=self.plan_obj.id, card=DUMMY_CARD, 332 | trial_end=trial_end.strftime('Y-m-d')) 333 | self.assertTrue(subscription.id) 334 | 335 | def test_integer_trial_end(self): 336 | trial_end_dttm = datetime.datetime.now() + datetime.timedelta(days=15) 337 | trial_end_int = int(time.mktime(trial_end_dttm.timetuple())) 338 | customer = openpay.Customer.create(name="Miguel", 339 | last_name="Lopez", 340 | email="mlopez@example.com") 341 | subscription = customer.subscriptions.create( 342 | plan_id=self.plan_obj.id, card=DUMMY_CARD, 343 | trial_end=trial_end_int) 344 | self.assertTrue(subscription.id) 345 | 346 | 347 | class InvalidRequestErrorTest(OpenpayTestCase): 348 | 349 | def test_nonexistent_object(self): 350 | try: 351 | openpay.Charge.retrieve('invalid') 352 | except openpay.error.InvalidRequestError as e: 353 | self.assertEqual(404, e.http_status) 354 | self.assertTrue(isinstance(e.http_body, str)) 355 | self.assertTrue(isinstance(e.json_body, dict)) 356 | 357 | def test_invalid_data(self): 358 | try: 359 | openpay.Charge.create() 360 | except openpay.error.InvalidRequestError as e: 361 | self.assertEqual(400, e.http_status) 362 | 363 | class PlanTest(OpenpayTestCase): 364 | 365 | def setUp(self): 366 | super(PlanTest, self).setUp() 367 | try: 368 | openpay.Plan(DUMMY_PLAN['id']).delete() 369 | except openpay.error.InvalidRequestError: 370 | pass 371 | 372 | def test_create_plan(self): 373 | self.assertRaises(openpay.error.InvalidRequestError, 374 | openpay.Plan.create, amount=250) 375 | p = openpay.Plan.create(**DUMMY_PLAN) 376 | self.assertTrue(hasattr(p, 'amount')) 377 | self.assertTrue(hasattr(p, 'id')) 378 | self.assertEqual(DUMMY_PLAN['amount'], p.amount) 379 | p.delete() 380 | self.assertEqual(list(p.keys()), []) 381 | # self.assertTrue(p.deleted) 382 | 383 | def test_update_plan(self): 384 | p = openpay.Plan.create(**DUMMY_PLAN) 385 | name = "New plan name" 386 | p.name = name 387 | p.save() 388 | self.assertEqual(name, p.name) 389 | p.delete() 390 | 391 | def test_update_plan_without_retrieving(self): 392 | p = openpay.Plan.create(**DUMMY_PLAN) 393 | 394 | name = 'updated plan name!' 395 | plan = openpay.Plan(p.id) 396 | plan.name = name 397 | 398 | # should only have name and id 399 | self.assertEqual(sorted(['id', 'name']), sorted(plan.keys())) 400 | plan.save() 401 | 402 | self.assertEqual(name, plan.name) 403 | # should load all the properties 404 | self.assertEqual(p.amount, plan.amount) 405 | p.delete() 406 | 407 | 408 | class PayoutTest(OpenpayTestCase): 409 | 410 | def setUp(self): 411 | super(PayoutTest, self).setUp() 412 | self.customer = openpay.Customer.create( 413 | name="John", last_name="Doe", description="Test User", 414 | email="johndoe@example.com") 415 | self.bank_account = self.customer.bank_accounts.create( 416 | clabe="646180109490002822", 417 | alias="Cuenta principal", 418 | holder_name="John Doe") 419 | 420 | self.card = self.customer.cards.create( 421 | card_number="4111111111111111", 422 | holder_name="Juan Perez", 423 | expiration_year="20", 424 | expiration_month="12", 425 | cvv2="110", 426 | address={ 427 | "city": "Querétaro", 428 | "country_code": "MX", 429 | "postal_code": "76900", 430 | "line1": "Av 5 de Febrero", 431 | "line2": "Roble 207", 432 | "line3": "col carrillo", 433 | "state": "Querétaro" 434 | } 435 | ) 436 | 437 | self.customer.charges.create(source_id=self.card.id, method="card", 438 | amount=100, description="Test Charge", 439 | order_id=generate_order_id()) 440 | 441 | def test_create_payout_with_bank_account(self): 442 | payout = self.customer.payouts.create( 443 | method='bank_account', 444 | destination_id=self.bank_account.id, 445 | amount="10", 446 | description="First payout", 447 | order_id=generate_order_id()) 448 | 449 | self.assertTrue(hasattr(payout, 'id')) 450 | self.assertTrue(isinstance(payout, openpay.Payout)) 451 | 452 | def test_list_all_payout(self): 453 | payout_list = self.customer.payouts.all() 454 | self.assertTrue(isinstance(payout_list.data, list)) 455 | self.assertEqual(len(payout_list.data), payout_list.count) 456 | 457 | 458 | class CardTest(OpenpayTestCase): 459 | 460 | def setUp(self): 461 | super(CardTest, self).setUp() 462 | self.customer = openpay.Customer.create( 463 | name="John", last_name="Doe", description="Test User", 464 | email="johndoe@example.com") 465 | self.card = self.customer.cards.create( 466 | card_number="4111111111111111", 467 | holder_name="Juan Perez", 468 | expiration_year="20", 469 | expiration_month="12", 470 | cvv2="110", 471 | address={ 472 | "city": "Querétaro", 473 | "country_code": "MX", 474 | "postal_code": "76900", 475 | "line1": "Av 5 de Febrero", 476 | "line2": "Roble 207", 477 | "line3": "col carrillo", 478 | "state": "Queretaro" 479 | } 480 | ) 481 | 482 | def test_card_created(self): 483 | self.assertTrue(isinstance(self.card, openpay.Card)) 484 | 485 | def test_card_list_all(self): 486 | card_list = self.customer.cards.all() 487 | self.assertEqual(card_list.count, 1) 488 | self.assertEqual(len(card_list.data), card_list.count) 489 | self.assertTrue(isinstance(card_list, openpay.resource.ListObject)) 490 | 491 | def test_card_retrieve(self): 492 | card_list = self.customer.cards.all() 493 | card = card_list.data[0] 494 | retrieved_card = self.customer.cards.retrieve(card.id) 495 | self.assertEqual(card.id, retrieved_card.id) 496 | 497 | def test_card_delete(self): 498 | self.card.delete() 499 | self.assertEqual(list(self.card.keys()), []) 500 | 501 | 502 | class FeeTest(OpenpayTestCase): 503 | 504 | def setUp(self): 505 | super(FeeTest, self).setUp() 506 | self.customer = openpay.Customer.create( 507 | name="John", last_name="Doe", description="Test User", 508 | email="johndoe@example.com") 509 | self.bank_account = self.customer.bank_accounts.create( 510 | clabe="646180109490002822", 511 | alias="Cuenta principal", 512 | holder_name="John Doe") 513 | 514 | self.card = self.customer.cards.create( 515 | card_number="4111111111111111", 516 | holder_name="Juan Perez", 517 | expiration_year="20", 518 | expiration_month="12", 519 | cvv2="110", 520 | address={ 521 | "city": "Querétaro", 522 | "country_code": "MX", 523 | "postal_code": "76900", 524 | "line1": "Av 5 de Febrero", 525 | "line2": "Roble 207", 526 | "line3": "col carrillo", 527 | "state": "Queretaro" 528 | } 529 | ) 530 | 531 | self.charge = self.customer.charges.create( 532 | source_id=self.card.id, method="card", 533 | amount=10, description="Test Charge", 534 | order_id=generate_order_id()) 535 | 536 | def test_fee_create(self): 537 | fee = openpay.Fee.create( 538 | customer_id=self.customer.id, amount=5, 539 | description="Test Fee", order_id=generate_order_id()) 540 | self.assertTrue(isinstance(fee, openpay.Fee)) 541 | self.assertTrue(hasattr(fee, 'id')) 542 | 543 | def test_fee_list_all(self): 544 | fee_list = openpay.Fee.all() 545 | self.assertTrue(isinstance(fee_list, openpay.resource.ListObject)) 546 | self.assertTrue(isinstance(fee_list.data, list)) 547 | self.assertEqual(fee_list.count, len(fee_list.data)) 548 | 549 | 550 | class TransferTest(OpenpayTestCase): 551 | 552 | def setUp(self): 553 | super(TransferTest, self).setUp() 554 | self.customer = openpay.Customer.create( 555 | name="John", last_name="Doe", description="Test User", 556 | email="johndoe@example.com") 557 | self.bank_account = self.customer.bank_accounts.create( 558 | clabe="646180109490002822", 559 | alias="Cuenta principal", 560 | holder_name="John Doe") 561 | 562 | self.card = self.customer.cards.create( 563 | card_number="4111111111111111", 564 | holder_name="Juan Perez", 565 | expiration_year="20", 566 | expiration_month="12", 567 | cvv2="110", 568 | address={ 569 | "city": "Querétaro", 570 | "country_code": "MX", 571 | "postal_code": "76900", 572 | "line1": "Av 5 de Febrero", 573 | "line2": "Roble 207", 574 | "line3": "col carrillo", 575 | "state": "Queretaro" 576 | } 577 | ) 578 | 579 | self.charge = self.customer.charges.create( 580 | source_id=self.card.id, method="card", 581 | amount=100, description="Test Charge", 582 | order_id=generate_order_id()) 583 | 584 | self.second_customer = openpay.Customer.all().data[3] 585 | 586 | def test_transfer_create(self): 587 | transfer = self.customer.transfers.create( 588 | customer_id=self.second_customer.id, amount=10, 589 | description="Test transfer", order_id=generate_order_id()) 590 | self.assertTrue(isinstance(transfer, openpay.Transfer)) 591 | self.assertTrue(hasattr(transfer, 'id')) 592 | 593 | def test_transfer_list_all(self): 594 | transfer_list = self.customer.transfers.all() 595 | self.assertTrue(isinstance(transfer_list, openpay.resource.ListObject)) 596 | self.assertTrue(isinstance(transfer_list.data, list)) 597 | self.assertEqual(transfer_list.count, len(transfer_list.data)) 598 | 599 | def test_transfer_retrieve(self): 600 | transfer = self.customer.transfers.create( 601 | customer_id=self.second_customer.id, amount=10, 602 | description="Test transfer", order_id=generate_order_id()) 603 | transfer_list = self.customer.transfers.all() 604 | test_transfer = transfer_list.data[0] 605 | transfer = self.customer.transfers.retrieve(test_transfer.id) 606 | self.assertTrue(isinstance(transfer, openpay.Transfer)) 607 | 608 | def test_list_transfers(self): 609 | customer = openpay.Customer.retrieve("amce5ycvwycfzyarjf8l") 610 | transfers = customer.transfers.all() 611 | self.assertTrue(isinstance(transfers.data, list)) 612 | self.assertTrue(isinstance(transfers.data[0], openpay.Transfer)) 613 | 614 | if __name__ == '__main__': 615 | unittest.main() 616 | -------------------------------------------------------------------------------- /openpay/test/test_resources.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import unicode_literals 3 | from future.builtins import super 4 | #import json 5 | import openpay 6 | #from openpay import util 7 | 8 | from openpay.test.helper import ( 9 | OpenpayUnitTestCase, OpenpayApiTestCase, 10 | MySingleton, MyListable, MyCreatable, MyUpdateable, MyDeletable, 11 | MyResource) 12 | 13 | 14 | class BaseObjectTests(OpenpayUnitTestCase): 15 | 16 | def test_initializes_with_parameters(self): 17 | obj = openpay.resource.BaseObject( 18 | 'foo', 'bar', myparam=5, yourparam='boo') 19 | 20 | self.assertEqual('foo', obj.id) 21 | self.assertEqual('bar', obj.api_key) 22 | 23 | def test_access(self): 24 | obj = openpay.resource.BaseObject('myid', 'mykey', myparam=5) 25 | 26 | # Empty 27 | self.assertRaises(AttributeError, getattr, obj, 'myattr') 28 | self.assertRaises(KeyError, obj.__getitem__, 'myattr') 29 | self.assertEqual('def', obj.get('myattr', 'def')) 30 | self.assertEqual(None, obj.get('myattr')) 31 | 32 | # Setters 33 | obj.myattr = 'myval' 34 | obj['myitem'] = 'itval' 35 | self.assertEqual('sdef', obj.setdefault('mydef', 'sdef')) 36 | 37 | # Getters 38 | self.assertEqual('myval', obj.setdefault('myattr', 'sdef')) 39 | self.assertEqual('myval', obj.myattr) 40 | self.assertEqual('myval', obj['myattr']) 41 | self.assertEqual('myval', obj.get('myattr')) 42 | 43 | self.assertEqual(['id', 'myattr', 'mydef', 'myitem'], 44 | sorted(obj.keys())) 45 | self.assertEqual(['itval', 'myid', 'myval', 'sdef'], 46 | sorted(obj.values())) 47 | 48 | # Illegal operations 49 | self.assertRaises(ValueError, setattr, obj, 'foo', '') 50 | self.assertRaises(TypeError, obj.__delitem__, 'myattr') 51 | 52 | def test_refresh_from(self): 53 | obj = openpay.resource.BaseObject.construct_from({ 54 | 'foo': 'bar', 55 | 'trans': 'me', 56 | }, 'mykey') 57 | 58 | self.assertEqual('mykey', obj.api_key) 59 | self.assertEqual('bar', obj.foo) 60 | self.assertEqual('me', obj['trans']) 61 | 62 | obj.refresh_from({ 63 | 'foo': 'baz', 64 | 'johnny': 5, 65 | }, 'key2') 66 | 67 | self.assertEqual(5, obj.johnny) 68 | self.assertEqual('baz', obj.foo) 69 | self.assertRaises(AttributeError, getattr, obj, 'trans') 70 | self.assertEqual('key2', obj.api_key) 71 | 72 | obj.refresh_from({ 73 | 'trans': 4, 74 | 'metadata': {'amount': 42} 75 | }, 'key2', True) 76 | 77 | self.assertEqual('baz', obj.foo) 78 | self.assertEqual(4, obj.trans) 79 | self.assertEqual({'amount': 42}, obj._previous_metadata) 80 | 81 | # def test_refresh_from_nested_object(self): 82 | # obj = openpay.resource.BaseObject.construct_from( 83 | # SAMPLE_INVOICE, 'key') 84 | # 85 | # self.assertEqual(1, len(obj.lines.subscriptions)) 86 | # self.assertTrue( 87 | # isinstance(obj.lines.subscriptions[0], 88 | # openpay.resource.BaseObject)) 89 | # self.assertEqual('month', obj.lines.subscriptions[0].plan.interval) 90 | 91 | # def test_to_json(self): 92 | # obj = openpay.resource.BaseObject.construct_from( 93 | # SAMPLE_INVOICE, 'key') 94 | # 95 | # self.check_invoice_data(json.loads(str(obj))) 96 | 97 | def check_invoice_data(self, data): 98 | # Check rough structure 99 | self.assertEqual(20, len(list(data.keys()))) 100 | self.assertEqual(3, len(list(data['lines'].keys()))) 101 | self.assertEqual(0, len(data['lines']['invoiceitems'])) 102 | self.assertEqual(1, len(data['lines']['subscriptions'])) 103 | 104 | # Check various data types 105 | self.assertEqual(1338238728, data['date']) 106 | self.assertEqual(None, data['next_payment_attempt']) 107 | self.assertEqual(False, data['livemode']) 108 | self.assertEqual('month', 109 | data['lines']['subscriptions'][0]['plan']['interval']) 110 | 111 | 112 | class ListObjectTests(OpenpayApiTestCase): 113 | 114 | def setUp(self): 115 | super(ListObjectTests, self).setUp() 116 | 117 | self.lo = openpay.resource.ListObject.construct_from({ 118 | 'id': 'me', 119 | 'url': '/my/path', 120 | 'item_type': 'charge' 121 | }, 'mykey') 122 | 123 | self.mock_response([{ 124 | 'foo': 'bar', 125 | }]) 126 | 127 | def assertResponse(self, res): 128 | self.assertTrue(isinstance(res.data[0], openpay.Charge)) 129 | self.assertEqual('bar', res.data[0].foo) 130 | 131 | def test_all(self): 132 | res = self.lo.all(myparam='you') 133 | 134 | self.requestor_mock.request.assert_called_with( 135 | 'get', '/my/path', {'myparam': 'you'}) 136 | 137 | self.assertResponse(res) 138 | 139 | def test_create(self): 140 | res = self.lo.create(myparam='eter') 141 | 142 | self.requestor_mock.request.assert_called_with( 143 | 'post', '/my/path', {'myparam': 'eter'}) 144 | 145 | self.assertResponse(res) 146 | 147 | def test_retrieve(self): 148 | res = self.lo.retrieve('myid', myparam='cow') 149 | 150 | self.requestor_mock.request.assert_called_with( 151 | 'get', '/my/path/myid', {'myparam': 'cow'}) 152 | 153 | self.assertResponse(res) 154 | 155 | 156 | class APIResourceTests(OpenpayApiTestCase): 157 | 158 | def test_retrieve_and_refresh(self): 159 | self.mock_response({ 160 | 'id': 'foo2', 161 | 'bobble': 'scrobble', 162 | }) 163 | 164 | res = MyResource.retrieve('foo*', myparam=5) 165 | 166 | url = '/v1/{0}/myresources/foo%2A'.format(openpay.merchant_id) 167 | self.requestor_mock.request.assert_called_with( 168 | 'get', url, {'myparam': 5} 169 | ) 170 | 171 | self.assertEqual('scrobble', res.bobble) 172 | self.assertEqual('foo2', res.id) 173 | self.assertEqual('reskey', res.api_key) 174 | 175 | self.mock_response({ 176 | 'frobble': 5, 177 | }) 178 | 179 | res = res.refresh() 180 | 181 | url = '/v1/{0}/myresources/foo2'.format(openpay.merchant_id) 182 | self.requestor_mock.request.assert_called_with( 183 | 'get', url, {'myparam': 5} 184 | ) 185 | 186 | self.assertEqual(5, res.frobble) 187 | self.assertRaises(KeyError, res.__getitem__, 'bobble') 188 | 189 | def test_convert_to_openpay_object(self): 190 | sample = { 191 | 'foo': 'bar', 192 | 'adict': { 193 | 'object': 'charge', 194 | 'id': 42, 195 | 'amount': 7, 196 | }, 197 | 'alist': [ 198 | { 199 | 'object': 'customer', 200 | 'name': 'chilango' 201 | } 202 | ] 203 | } 204 | 205 | converted = openpay.resource.convert_to_openpay_object(sample, 'akey') 206 | 207 | # Types 208 | self.assertTrue(isinstance(converted, openpay.resource.BaseObject)) 209 | self.assertTrue(isinstance(converted.adict, openpay.Charge)) 210 | self.assertEqual(1, len(converted.alist)) 211 | self.assertTrue(isinstance(converted.alist[0], openpay.Customer)) 212 | 213 | # Values 214 | self.assertEqual('bar', converted.foo) 215 | self.assertEqual(42, converted.adict.id) 216 | self.assertEqual('chilango', converted.alist[0].name) 217 | 218 | # Stripping 219 | # TODO: We should probably be stripping out this property 220 | # self.assertRaises(AttributeError, getattr, converted.adict, 'object') 221 | 222 | 223 | class SingletonAPIResourceTests(OpenpayApiTestCase): 224 | 225 | def test_retrieve(self): 226 | self.mock_response({ 227 | 'single': 'ton' 228 | }) 229 | res = MySingleton.retrieve() 230 | url = '/v1/{0}/mysingleton'.format(openpay.merchant_id) 231 | self.requestor_mock.request.assert_called_with( 232 | 'get', url, {}) 233 | 234 | self.assertEqual('ton', res.single) 235 | 236 | 237 | class ListableAPIResourceTests(OpenpayApiTestCase): 238 | 239 | def test_all(self): 240 | self.mock_response([ 241 | { 242 | 'object': 'charge', 243 | 'name': 'jose', 244 | }, 245 | { 246 | 'object': 'charge', 247 | 'name': 'curly', 248 | } 249 | ]) 250 | 251 | res = MyListable.all() 252 | url = '/v1/{0}/mylistables'.format(openpay.merchant_id) 253 | self.requestor_mock.request.assert_called_with( 254 | 'get', url, {}) 255 | 256 | self.assertEqual(2, len(res.data)) 257 | self.assertTrue(all( 258 | isinstance(obj, openpay.Charge) for obj in res.data)) 259 | self.assertEqual('jose', res.data[0].name) 260 | self.assertEqual('curly', res.data[1].name) 261 | 262 | 263 | class CreateableAPIResourceTests(OpenpayApiTestCase): 264 | 265 | def test_create(self): 266 | self.mock_response({ 267 | 'object': 'charge', 268 | 'foo': 'bar', 269 | }) 270 | 271 | res = MyCreatable.create() 272 | url = '/v1/{0}/mycreatables'.format(openpay.merchant_id) 273 | self.requestor_mock.request.assert_called_with( 274 | 'post', url, {}) 275 | 276 | self.assertTrue(isinstance(res, openpay.Charge)) 277 | self.assertEqual('bar', res.foo) 278 | 279 | 280 | class UpdateableAPIResourceTests(OpenpayApiTestCase): 281 | 282 | def setUp(self): 283 | super(UpdateableAPIResourceTests, self).setUp() 284 | 285 | self.mock_response({ 286 | 'thats': 'it' 287 | }) 288 | 289 | self.obj = MyUpdateable.construct_from({ 290 | 'id': 'myid', 291 | 'foo': 'bar', 292 | 'baz': 'boz', 293 | 'metadata': { 294 | 'size': 'l', 295 | 'score': 4, 296 | 'height': 10 297 | } 298 | }, 'mykey') 299 | 300 | def checkSave(self): 301 | self.assertTrue(self.obj is self.obj.save()) 302 | 303 | self.assertEqual('it', self.obj.thats) 304 | # TODO: Should we force id to be retained? 305 | # self.assertEqual('myid', obj.id) 306 | self.assertRaises(AttributeError, getattr, self.obj, 'baz') 307 | 308 | def test_save(self): 309 | self.obj.baz = 'updated' 310 | self.obj.other = 'newval' 311 | self.obj.metadata.size = 'm' 312 | self.obj.metadata.info = 'a2' 313 | self.obj.metadata.height = None 314 | 315 | self.checkSave() 316 | 317 | self.requestor_mock.request.assert_called_with( 318 | 'put', 319 | '/v1/{0}/myupdateables/myid'.format(openpay.merchant_id), 320 | MyUpdateable.construct_from({ 321 | 'id': 'myid', 322 | 'foo': 'bar', 323 | 'baz': 'updated', 324 | 'other': 'newval', 325 | 'status': None, 326 | 'metadata': { 327 | 'size': 'm', 328 | 'info': 'a2', 329 | 'height': None, 330 | 'score': 4 331 | } 332 | }, 'mykey') 333 | ) 334 | 335 | def test_save_replace_metadata(self): 336 | self.obj.baz = 'updated' 337 | self.obj.other = 'newval' 338 | self.obj.metadata = { 339 | 'size': 'm', 340 | 'info': 'a2', 341 | 'score': 4, 342 | } 343 | 344 | self.checkSave() 345 | 346 | self.requestor_mock.request.assert_called_with( 347 | 'put', 348 | '/v1/{0}/myupdateables/myid'.format(openpay.merchant_id), 349 | MyUpdateable.construct_from({ 350 | 'baz': 'updated', 351 | 'other': 'newval', 352 | 'id': 'myid', 353 | 'foo': 'bar', 354 | 'status': None, 355 | 'metadata': { 356 | 'size': 'm', 357 | 'info': 'a2', 358 | 'height': '', 359 | 'score': 4, 360 | } 361 | }, 'mykey') 362 | ) 363 | 364 | 365 | class DeletableAPIResourceTests(OpenpayApiTestCase): 366 | 367 | def test_delete(self): 368 | self.mock_response({ 369 | 'id': 'mid', 370 | 'deleted': True, 371 | }) 372 | 373 | obj = MyDeletable.construct_from({ 374 | 'id': 'mid' 375 | }, 'mykey') 376 | 377 | self.assertTrue(obj is obj.delete()) 378 | 379 | self.assertEqual(True, obj.deleted) 380 | self.assertEqual('mid', obj.id) 381 | -------------------------------------------------------------------------------- /openpay/testCo/__init__.py: -------------------------------------------------------------------------------- 1 | import pkgutil 2 | import unittest 3 | 4 | 5 | def all_names(): 6 | for _, modname, _ in pkgutil.iter_modules(__path__): 7 | if modname.startswith('test_'): 8 | yield 'openpay.testCo.' + modname 9 | 10 | 11 | def all(): 12 | return unittest.defaultTestLoader.loadTestsFromNames(all_names()) 13 | 14 | 15 | def unit(): 16 | unit_names = [name for name in all_names() if 'integration' not in name] 17 | return unittest.defaultTestLoader.loadTestsFromNames(unit_names) 18 | -------------------------------------------------------------------------------- /openpay/testCo/helperco.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import datetime 5 | import os 6 | import random 7 | import re 8 | import string 9 | import sys 10 | import time 11 | import unittest 12 | 13 | from future.builtins import range 14 | from future.builtins import str 15 | from future.builtins import super 16 | from mock import patch, Mock 17 | 18 | import openpay 19 | 20 | 21 | def generate_order_id(): 22 | order_id = 'oid-test-{0}-{1}'.format( 23 | random.randint(1, 3000), str(time.time())[7:]) 24 | if len(order_id) > 20: 25 | order_id = order_id[:20] 26 | 27 | return order_id 28 | 29 | 30 | NOW = datetime.datetime.now() 31 | DUMMY_CUSTOMER = { 32 | "name": "Cliente Colombia", 33 | "last_name": "Vazquez Juarez", 34 | "phone_number": "4448936475", 35 | "email": "juan.vazquez@empresa.co" 36 | } 37 | DUMMY_CARD = { 38 | 'holder_name': 'Juan Lopez', 39 | 'card_number': '4111111111111111', 40 | "cvv2": "110", 41 | 'expiration_month': NOW.month, 42 | 'expiration_year': str(NOW.year + 4)[2:] 43 | } 44 | 45 | DUMMY_CHARGE = { 46 | 'method': 'card', 47 | 'amount': 100, 48 | 'currency': 'COP', 49 | 'iva': '10', 50 | 'description': 'Dummy Charge' 51 | } 52 | 53 | DUMMY_CHARGE_MERCHANT = { 54 | 'method': 'card', 55 | 'amount': 100, 56 | 'currency': 'COP', 57 | 'iva': '10', 58 | 'description': 'Dummy Charge', 59 | 'confirm': 'false', 60 | 'redirect_url': 'http://testing-openpay-col.com', 61 | 'customer': DUMMY_CUSTOMER 62 | } 63 | 64 | DUMMY_CHARGE_STORE = { 65 | 'method': 'store', 66 | 'amount': 100, 67 | 'currency': 'COP', 68 | 'iva': '10', 69 | 'description': 'Store Charge' 70 | } 71 | 72 | DUMMY_CHARGE_STORE_MERCHANT = { 73 | 'method': 'store', 74 | 'amount': 100, 75 | 'currency': 'COP', 76 | 'iva': '10', 77 | 'description': 'Store Charge', 78 | 'customer': DUMMY_CUSTOMER 79 | } 80 | 81 | DUMMY_PLAN = { 82 | 'name': 'Amazing Gold Plan', 83 | 'amount': 2000, 84 | 'repeat_every': 1, 85 | 'repeat_unit': 'month', 86 | 'retry_times': 2, 87 | 'status_after_retry': 'cancelled', 88 | 'trial_days': 0 89 | 90 | } 91 | 92 | DUMMY_TRANSFER = { 93 | 'amount': 400, 94 | 'customer_id': 'acuqxruyv0hi1wfdwmym', 95 | 'description': 'Dummy Transfer', 96 | 'order_id': 'oid-00099', 97 | } 98 | 99 | DUMMY_PSE = { 100 | 'method': 'bank_account', 101 | 'amount': 100, 102 | 'currency': 'COP', 103 | 'iva': '10', 104 | 'description': 'Dummy PSE', 105 | 'confirm': 'false', 106 | 'redirect_url': 'http://testing-openpay-col.com' 107 | } 108 | 109 | DUMMY_PSE_MERCHANT = { 110 | 'method': 'bank_account', 111 | 'amount': 100, 112 | 'currency': 'COP', 113 | 'iva': '10', 114 | 'description': 'Dummy PSE', 115 | 'confirm': 'false', 116 | 'redirect_url': 'http://testing-openpay-col.com', 117 | 'customer': DUMMY_CUSTOMER 118 | } 119 | 120 | DUMMY_ADDRESS = { 121 | "city": "Bogotá", 122 | "country_code": "CO", 123 | "postal_code": "110511", 124 | "line1": "Av 5 de Febrero", 125 | "line2": "Roble 207", 126 | "line3": "col carrillo", 127 | "state": "Bogota" 128 | } 129 | 130 | 131 | class OpenpayTestCase(unittest.TestCase): 132 | RESTORE_ATTRIBUTES = ('api_version', 'api_key') 133 | 134 | def setUp(self): 135 | super(OpenpayTestCase, self).setUp() 136 | 137 | self._openpay_original_attributes = {} 138 | 139 | for attr in self.RESTORE_ATTRIBUTES: 140 | self._openpay_original_attributes[attr] = getattr(openpay, attr) 141 | 142 | api_base = os.environ.get('OPENPAY_API_BASE') 143 | if api_base: 144 | openpay.api_base = api_base 145 | # Sandbox 146 | openpay.api_key = os.environ.get( 147 | 'OPENPAY_API_KEY', 'sk_94a89308b4d7469cbda762c4b392152a') 148 | openpay.merchant_id = "mwf7x79goz7afkdbuyqd" 149 | openpay.country = "co" 150 | # Dev 151 | # openpay.api_key = os.environ.get( 152 | # 'OPENPAY_API_KEY', '68df281c16184d47bb773d70abd4191b') 153 | # openpay.merchant_id = "m4se8bd4fef1mkzk6d1b" 154 | openpay.verify_ssl_certs = False 155 | 156 | def tearDown(self): 157 | super(OpenpayTestCase, self).tearDown() 158 | 159 | for attr in self.RESTORE_ATTRIBUTES: 160 | setattr(openpay, attr, self._openpay_original_attributes[attr]) 161 | 162 | # Python < 2.7 compatibility 163 | def assertRaisesRegexp(self, exception, regexp, callable, *args, **kwargs): 164 | try: 165 | callable(*args, **kwargs) 166 | except exception as err: 167 | if regexp is None: 168 | return True 169 | 170 | if isinstance(regexp, str): 171 | regexp = re.compile(regexp) 172 | if not regexp.search(str(err)): 173 | raise self.failureException('"%s" does not match "%s"' % 174 | (regexp.pattern, str(err))) 175 | else: 176 | raise self.failureException( 177 | '%s was not raised' % (exception.__name__,)) 178 | 179 | 180 | class OpenpayUnitTestCase(OpenpayTestCase): 181 | REQUEST_LIBRARIES = ['urlfetch', 'requests', 'pycurl'] 182 | 183 | if sys.version_info >= (3, 0): 184 | REQUEST_LIBRARIES.append('urllib.request') 185 | else: 186 | REQUEST_LIBRARIES.append('urllib2') 187 | 188 | def setUp(self): 189 | super(OpenpayUnitTestCase, self).setUp() 190 | 191 | self.request_patchers = {} 192 | self.request_mocks = {} 193 | for lib in self.REQUEST_LIBRARIES: 194 | patcher = patch("openpay.http_client.%s" % (lib,)) 195 | 196 | self.request_mocks[lib] = patcher.start() 197 | self.request_patchers[lib] = patcher 198 | 199 | def tearDown(self): 200 | super(OpenpayUnitTestCase, self).tearDown() 201 | 202 | for patcher in list(self.request_patchers.values()): 203 | patcher.stop() 204 | 205 | 206 | class OpenpayApiTestCase(OpenpayTestCase): 207 | 208 | def setUp(self): 209 | super(OpenpayApiTestCase, self).setUp() 210 | 211 | self.requestor_patcher = patch('openpay.api.APIClient') 212 | requestor_class_mock = self.requestor_patcher.start() 213 | self.requestor_mock = requestor_class_mock.return_value 214 | 215 | def tearDown(self): 216 | super(OpenpayApiTestCase, self).tearDown() 217 | 218 | self.requestor_patcher.stop() 219 | 220 | def mock_response(self, res): 221 | self.requestor_mock.request = Mock(return_value=(res, 'reskey')) 222 | 223 | 224 | class MyResource(openpay.resource.APIResource): 225 | pass 226 | 227 | 228 | class MySingleton(openpay.resource.SingletonAPIResource): 229 | pass 230 | 231 | 232 | class MyListable(openpay.resource.ListableAPIResource): 233 | pass 234 | 235 | 236 | class MyCreatable(openpay.resource.CreateableAPIResource): 237 | pass 238 | 239 | 240 | class MyUpdateable(openpay.resource.UpdateableAPIResource): 241 | pass 242 | 243 | 244 | class MyDeletable(openpay.resource.DeletableAPIResource): 245 | pass 246 | 247 | 248 | class MyComposite(openpay.resource.ListableAPIResource, 249 | openpay.resource.CreateableAPIResource, 250 | openpay.resource.UpdateableAPIResource, 251 | openpay.resource.DeletableAPIResource): 252 | pass 253 | -------------------------------------------------------------------------------- /openpay/testCo/test_co_integration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import datetime 5 | import os 6 | import sys 7 | import time 8 | import unittest 9 | 10 | from future.builtins import int 11 | from future.builtins import str 12 | from future.builtins import super 13 | from future.builtins import zip 14 | from mock import patch 15 | 16 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) 17 | import openpay 18 | 19 | from openpay.testCo.helperco import ( 20 | OpenpayTestCase, 21 | NOW, DUMMY_CARD, DUMMY_CHARGE, DUMMY_PLAN, 22 | DUMMY_CHARGE_STORE, generate_order_id, DUMMY_CUSTOMER, DUMMY_ADDRESS) 23 | 24 | 25 | class FunctionalTests(OpenpayTestCase): 26 | request_client = openpay.http_client.Urllib2Client 27 | 28 | def setUp(self): 29 | super(FunctionalTests, self).setUp() 30 | 31 | def get_http_client(*args, **kwargs): 32 | return self.request_client(*args, **kwargs) 33 | 34 | self.client_patcher = patch( 35 | 'openpay.http_client.new_default_http_client') 36 | 37 | client_mock = self.client_patcher.start() 38 | client_mock.side_effect = get_http_client 39 | 40 | def tearDown(self): 41 | super(FunctionalTests, self).tearDown() 42 | 43 | self.client_patcher.stop() 44 | 45 | def test_dns_failure(self): 46 | self.patched_api_base = patch( 47 | 'openpay.get_api_base', 48 | lambda: 'https://my-invalid-domain.ireallywontresolve/v1') 49 | # get_api_base_mock = self.patched_api_base.start() 50 | self.patched_api_base.start() 51 | try: 52 | self.assertRaises(openpay.error.APIConnectionError, 53 | openpay.Customer.create) 54 | finally: 55 | self.patched_api_base.stop() 56 | 57 | def test_run(self): 58 | DUMMY_CHARGE['order_id'] = generate_order_id() 59 | charge = openpay.Charge.create(**DUMMY_CHARGE) 60 | # self.assertFalse(hasattr(charge, 'refund')) 61 | charge.refund(merchant=True) 62 | self.assertTrue(hasattr(charge, 'refund')) 63 | 64 | def test_refresh(self): 65 | DUMMY_CHARGE['order_id'] = generate_order_id() 66 | charge = openpay.Charge.create(**DUMMY_CHARGE) 67 | charge2 = openpay.Charge.retrieve_as_merchant(charge.id) 68 | self.assertEqual(charge2.creation_date, charge.creation_date) 69 | 70 | charge2.junk = 'junk' 71 | charge2.refresh() 72 | self.assertRaises(AttributeError, lambda: charge2.junk) 73 | 74 | def test_list_accessors(self): 75 | customer = openpay.Customer.create( 76 | name="Miguel Lopez", email="mlopez@example.com") 77 | self.assertEqual(customer['creation_date'], customer.creation_date) 78 | customer['foo'] = 'bar' 79 | self.assertEqual(customer.foo, 'bar') 80 | 81 | def test_raise(self): 82 | EXPIRED_CARD = DUMMY_CARD.copy() 83 | expiration_year = NOW.year - 2 84 | EXPIRED_CARD['expiration_month'] = NOW.month 85 | EXPIRED_CARD['expiration_year'] = str(expiration_year)[2:] 86 | self.assertRaises(openpay.error.InvalidRequestError, openpay.Charge.create, 87 | amount=100, method='card', description="Test Order", 88 | order_id="oid-00080", card=EXPIRED_CARD) 89 | 90 | def test_unicode(self): 91 | # Make sure unicode requests can be sent 92 | self.assertRaises(openpay.error.InvalidRequestError, 93 | openpay.Charge.retrieve_as_merchant, 94 | id=u'☃') 95 | 96 | # def test_none_values(self): 97 | # customer = openpay.Customer.create(name=None, last_name=None) 98 | # self.assertTrue(customer.id) 99 | 100 | def test_missing_id(self): 101 | customer = openpay.Customer() 102 | self.assertRaises(openpay.error.InvalidRequestError, customer.refresh) 103 | 104 | 105 | class RequestsFunctionalTests(FunctionalTests): 106 | request_client = openpay.http_client.RequestsClient 107 | 108 | 109 | # Avoid skipTest errors in < 2.7 110 | if sys.version_info >= (2, 7): 111 | class UrlfetchFunctionalTests(FunctionalTests): 112 | request_client = 'urlfetch' 113 | 114 | def setUp(self): 115 | if openpay.http_client.urlfetch is None: 116 | self.skipTest( 117 | '`urlfetch` from Google App Engine is unavailable.') 118 | else: 119 | super(UrlfetchFunctionalTests, self).setUp() 120 | 121 | 122 | # class PycurlFunctionalTests(FunctionalTests): 123 | # def setUp(self): 124 | # if sys.version_info >= (3, 0): 125 | # self.skipTest('Pycurl is not supported in Python 3') 126 | # else: 127 | # super(PycurlFunctionalTests, self).setUp() 128 | 129 | # request_client = openpay.http_client.PycurlClient 130 | 131 | 132 | class AuthenticationErrorTest(OpenpayTestCase): 133 | 134 | def test_invalid_credentials(self): 135 | key = openpay.api_key 136 | try: 137 | openpay.api_key = 'invalid' 138 | openpay.Customer.create() 139 | except openpay.error.AuthenticationError as e: 140 | self.assertEqual(401, e.http_status) 141 | self.assertTrue(isinstance(e.http_body, str)) 142 | self.assertTrue(isinstance(e.json_body, dict)) 143 | finally: 144 | openpay.api_key = key 145 | 146 | 147 | class CardErrorTest(OpenpayTestCase): 148 | 149 | def test_declined_card_props(self): 150 | EXPIRED_CARD = DUMMY_CARD.copy() 151 | expiration_year = NOW.year - 2 152 | EXPIRED_CARD['expiration_month'] = NOW.month 153 | EXPIRED_CARD['expiration_year'] = str(expiration_year)[2:] 154 | try: 155 | openpay.Charge.create(amount=100, 156 | method='card', 157 | description="Test Order", 158 | order_id="oid-00080", 159 | card=EXPIRED_CARD) 160 | except openpay.error.InvalidRequestError as e: 161 | self.assertEqual(400, e.http_status) 162 | self.assertTrue(isinstance(e.http_body, str)) 163 | self.assertTrue(isinstance(e.json_body, dict)) 164 | 165 | 166 | # Note that these are in addition to the core functional charge tests 167 | # OK 168 | class ChargeTest(OpenpayTestCase): 169 | 170 | def setUp(self): 171 | super(ChargeTest, self).setUp() 172 | 173 | def test_charge_list_all(self): 174 | charge_list = openpay.Charge.all( 175 | creation={'lte': NOW.strftime("%Y-%m-%d")}) 176 | list_result = charge_list.all( 177 | creation={'lte': NOW.strftime("%Y-%m-%d")}) 178 | 179 | self.assertEqual(len(charge_list.data), 180 | len(list_result.data)) 181 | 182 | for expected, actual in zip(charge_list.data, 183 | list_result.data): 184 | self.assertEqual(expected.id, actual.id) 185 | 186 | def test_charge_with_token(self): 187 | token = openpay.Token.create(card_number='4111111111111111', holder_name='Juan Perez Ramirez', 188 | expiration_year='28', expiration_month='12', cvv2='110', address=DUMMY_ADDRESS) 189 | charge_list = openpay.Charge.all() 190 | DUMMY_CHARGE['device_session_id'] = generate_order_id() 191 | DUMMY_CHARGE['source_id'] = token.id 192 | DUMMY_CHARGE['customer'] = DUMMY_CUSTOMER 193 | charge = charge_list.create(**DUMMY_CHARGE) 194 | self.assertTrue(isinstance(charge, openpay.Charge)) 195 | self.assertEqual(DUMMY_CHARGE['amount'], charge.amount) 196 | 197 | def test_charge_with_token_and_customer(self): 198 | customer = openpay.Customer.create(name="Miguel Lopez", email="mlopez@example.com") 199 | token = openpay.Token.create(card_number='4111111111111111', 200 | holder_name='Juan Perez Ramirez', 201 | expiration_year='28', expiration_month='12', cvv2='110', address=DUMMY_ADDRESS) 202 | DUMMY_CHARGE['device_session_id'] = generate_order_id() 203 | DUMMY_CHARGE['source_id'] = token.id 204 | charge = openpay.Charge.create(customer=customer.id, **DUMMY_CHARGE) 205 | self.assertTrue(isinstance(charge, openpay.Charge)) 206 | self.assertEqual(DUMMY_CHARGE['amount'], charge.amount) 207 | 208 | def test_charge_list_retrieve(self): 209 | charge_list = openpay.Charge.all() 210 | charge = charge_list.retrieve(charge_list.data[0].id) 211 | self.assertTrue(isinstance(charge, openpay.Charge)) 212 | 213 | def test_charge_store_as_customer(self): 214 | customer = openpay.Customer.create( 215 | name="Miguel Lopez", email="mlopez@example.com") 216 | charge = customer.charges.create(**DUMMY_CHARGE_STORE) 217 | self.assertTrue(hasattr(charge, 'payment_method')) 218 | self.assertTrue(hasattr(charge.payment_method, 'reference')) 219 | self.assertTrue(hasattr(charge.payment_method, 'barcode_url')) 220 | self.assertEqual( 221 | customer.charges.retrieve(charge.id).status, 222 | 'in_progress') 223 | 224 | def test_charge_store_as_merchant(self): 225 | DUMMY_CHARGE_STORE['customer'] = DUMMY_CUSTOMER 226 | charge = openpay.Charge.create_as_merchant(**DUMMY_CHARGE_STORE) 227 | self.assertTrue(hasattr(charge, 'payment_method')) 228 | self.assertTrue(hasattr(charge.payment_method, 'reference')) 229 | self.assertTrue(hasattr(charge.payment_method, 'barcode_url')) 230 | self.assertEqual(charge.payment_method.type, "store") 231 | self.assertTrue(isinstance(charge, openpay.Charge)) 232 | self.assertEqual( 233 | openpay.Charge.retrieve_as_merchant(charge.id).status, 234 | 'in_progress') 235 | 236 | 237 | class CustomerTest(OpenpayTestCase): 238 | 239 | def test_list_customers(self): 240 | customers = openpay.Customer.all() 241 | self.assertTrue(isinstance(customers.data, list)) 242 | 243 | def test_list_charges(self): 244 | customer = openpay.Customer.create( 245 | name="Miguel Lopez", 246 | last_name="Mi last name", 247 | email="col@example.com", 248 | phone_number="5744484951", 249 | description="foo bar") 250 | token = openpay.Token.create(card_number='4111111111111111', 251 | holder_name='Juan Perez Ramirez', 252 | expiration_year='28', expiration_month='12', cvv2='110', address=DUMMY_ADDRESS) 253 | customer.charges.create( 254 | source_id=token.id, 255 | method="card", 256 | amount=100, 257 | currency="COP", 258 | iva="10", 259 | description="Customer test charge", 260 | device_session_id=generate_order_id()) 261 | 262 | self.assertEqual(1, len(customer.charges.all().data)) 263 | 264 | def test_pse(self): 265 | customer = openpay.Customer.create( 266 | name="Miguel Lopez", 267 | last_name="Mi last name", 268 | email="col@example.com", 269 | phone_number="5744484951", 270 | description="foo bar") 271 | pse = customer.pse.create(method='bank_account', 272 | amount=100, 273 | currency='COP', 274 | iva='10', 275 | description='Dummy PSE', 276 | redirect_url='/') 277 | 278 | self.assertEqual(pse.method, 'bank_account') 279 | 280 | 281 | # ok 282 | class CustomerPlanTest(OpenpayTestCase): 283 | 284 | def setUp(self): 285 | super(CustomerPlanTest, self).setUp() 286 | try: 287 | self.plan_obj = openpay.Plan.create(**DUMMY_PLAN) 288 | except openpay.error.InvalidRequestError: 289 | self.plan_obj = None 290 | 291 | def tearDown(self): 292 | if self.plan_obj: 293 | try: 294 | self.plan_obj.delete() 295 | except openpay.error.InvalidRequestError: 296 | pass 297 | super(CustomerPlanTest, self).tearDown() 298 | 299 | def test_create_customer(self): 300 | self.assertRaises(openpay.error.InvalidRequestError, 301 | openpay.Customer.create, 302 | plan=DUMMY_PLAN['id']) 303 | customer = openpay.Customer.create( 304 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 305 | 306 | subscription = customer.subscriptions.create( 307 | plan_id=self.plan_obj.id, card=DUMMY_CARD) 308 | 309 | self.assertTrue(isinstance(subscription, openpay.Subscription)) 310 | subscription.delete() 311 | self.assertFalse(hasattr(customer, 'subscription')) 312 | self.assertFalse(hasattr(customer, 'plan')) 313 | 314 | def test_update_and_cancel_subscription(self): 315 | customer = openpay.Customer.create( 316 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 317 | 318 | sub = customer.subscriptions.create( 319 | plan_id=self.plan_obj.id, card=DUMMY_CARD) 320 | 321 | sub.cancel_at_period_end = True 322 | sub.save() 323 | self.assertEqual(sub.status, 'active') 324 | self.assertTrue(sub.cancel_at_period_end) 325 | sub.delete() 326 | 327 | def test_datetime_trial_end(self): 328 | trial_end = datetime.datetime.now() + datetime.timedelta(days=15) 329 | customer = openpay.Customer.create( 330 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 331 | subscription = customer.subscriptions.create( 332 | plan_id=self.plan_obj.id, card=DUMMY_CARD, 333 | trial_end=trial_end.strftime('Y-m-d')) 334 | self.assertTrue(subscription.id) 335 | 336 | def test_integer_trial_end(self): 337 | trial_end_dttm = datetime.datetime.now() + datetime.timedelta(days=15) 338 | trial_end_int = int(time.mktime(trial_end_dttm.timetuple())) 339 | customer = openpay.Customer.create(name="Miguel", 340 | last_name="Lopez", 341 | email="mlopez@example.com") 342 | subscription = customer.subscriptions.create( 343 | plan_id=self.plan_obj.id, card=DUMMY_CARD, 344 | trial_end=trial_end_int) 345 | self.assertTrue(subscription.id) 346 | 347 | 348 | # ok 349 | class PlanTest(OpenpayTestCase): 350 | 351 | def setUp(self): 352 | super(PlanTest, self).setUp() 353 | 354 | def test_create_plan(self): 355 | self.assertRaises(openpay.error.InvalidRequestError, 356 | openpay.Plan.create, amount=250) 357 | p = openpay.Plan.create(**DUMMY_PLAN) 358 | self.assertTrue(hasattr(p, 'amount')) 359 | self.assertTrue(hasattr(p, 'id')) 360 | self.assertEqual(DUMMY_PLAN['amount'], p.amount) 361 | p.delete() 362 | self.assertEqual(list(p.keys()), []) 363 | # self.assertTrue(p.deleted) 364 | 365 | def test_update_plan(self): 366 | p = openpay.Plan.create(**DUMMY_PLAN) 367 | name = "New plan name" 368 | p.name = name 369 | p.save() 370 | self.assertEqual(name, p.name) 371 | p.delete() 372 | 373 | def test_update_plan_without_retrieving(self): 374 | p = openpay.Plan.create(**DUMMY_PLAN) 375 | 376 | name = 'updated plan name!' 377 | plan = openpay.Plan(p.id) 378 | plan.name = name 379 | 380 | # should only have name and id 381 | self.assertEqual(sorted(['id', 'name']), sorted(plan.keys())) 382 | plan.save() 383 | 384 | self.assertEqual(name, plan.name) 385 | # should load all the properties 386 | self.assertEqual(p.amount, plan.amount) 387 | p.delete() 388 | 389 | 390 | class CardTest(OpenpayTestCase): 391 | 392 | def setUp(self): 393 | super(CardTest, self).setUp() 394 | self.customer = openpay.Customer.create( 395 | name="John", last_name="Doe", description="Test User", 396 | email="johndoe@example.com") 397 | self.card = self.customer.cards.create( 398 | card_number="4111111111111111", 399 | holder_name="Juan Perez", 400 | expiration_year="25", 401 | expiration_month="12", 402 | cvv2="110", 403 | address={ 404 | "city": "Querétaro", 405 | "country_code": "MX", 406 | "postal_code": "76900", 407 | "line1": "Av 5 de Febrero", 408 | "line2": "Roble 207", 409 | "line3": "col carrillo", 410 | "state": "Queretaro" 411 | } 412 | ) 413 | 414 | def test_card_created(self): 415 | self.assertTrue(isinstance(self.card, openpay.Card)) 416 | 417 | def test_card_list_all(self): 418 | card_list = self.customer.cards.all() 419 | self.assertEqual(card_list.count, 1) 420 | self.assertEqual(len(card_list.data), card_list.count) 421 | self.assertTrue(isinstance(card_list, openpay.resource.ListObject)) 422 | 423 | def test_card_retrieve(self): 424 | card_list = self.customer.cards.all() 425 | card = card_list.data[0] 426 | retrieved_card = self.customer.cards.retrieve(card.id) 427 | self.assertEqual(card.id, retrieved_card.id) 428 | 429 | def test_card_delete(self): 430 | self.card.delete() 431 | self.assertEqual(list(self.card.keys()), []) 432 | 433 | 434 | class PSETest(OpenpayTestCase): 435 | 436 | def test_create(self): 437 | pse = openpay.Pse.create(method='bank_account', 438 | amount=100, 439 | currency='COP', 440 | iva='10', 441 | description='Dummy PSE', 442 | redirect_url='/', 443 | customer=DUMMY_CUSTOMER) 444 | self.assertEqual(pse.method, 'bank_account') 445 | self.assertIsNotNone(pse.customer) 446 | 447 | def test_create_with_customer(self): 448 | customer = openpay.Customer.create( 449 | name="Miguel Lopez", 450 | last_name="Mi last name", 451 | email="col@example.com", 452 | phone_number="5744484951", 453 | description="foo bar") 454 | 455 | pse = openpay.Pse.create(customer_id=customer.id, method='bank_account', 456 | amount=100, 457 | currency='COP', 458 | iva='10', 459 | description='Dummy PSE', 460 | redirect_url='/') 461 | self.assertEqual(pse.method, 'bank_account') 462 | 463 | 464 | if __name__ == '__main__': 465 | unittest.main() 466 | -------------------------------------------------------------------------------- /openpay/testCo/test_co_resources.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import unicode_literals 3 | from future.builtins import super 4 | #import json 5 | import openpay 6 | #from openpay import util 7 | 8 | from openpay.test.helper import ( 9 | OpenpayUnitTestCase, OpenpayApiTestCase, 10 | MySingleton, MyListable, MyCreatable, MyUpdateable, MyDeletable, 11 | MyResource) 12 | 13 | 14 | class BaseObjectTests(OpenpayUnitTestCase): 15 | 16 | def test_initializes_with_parameters(self): 17 | obj = openpay.resource.BaseObject( 18 | 'foo', 'bar', myparam=5, yourparam='boo') 19 | 20 | self.assertEqual('foo', obj.id) 21 | self.assertEqual('bar', obj.api_key) 22 | 23 | def test_access(self): 24 | obj = openpay.resource.BaseObject('myid', 'mykey', myparam=5) 25 | 26 | # Empty 27 | self.assertRaises(AttributeError, getattr, obj, 'myattr') 28 | self.assertRaises(KeyError, obj.__getitem__, 'myattr') 29 | self.assertEqual('def', obj.get('myattr', 'def')) 30 | self.assertEqual(None, obj.get('myattr')) 31 | 32 | # Setters 33 | obj.myattr = 'myval' 34 | obj['myitem'] = 'itval' 35 | self.assertEqual('sdef', obj.setdefault('mydef', 'sdef')) 36 | 37 | # Getters 38 | self.assertEqual('myval', obj.setdefault('myattr', 'sdef')) 39 | self.assertEqual('myval', obj.myattr) 40 | self.assertEqual('myval', obj['myattr']) 41 | self.assertEqual('myval', obj.get('myattr')) 42 | 43 | self.assertEqual(['id', 'myattr', 'mydef', 'myitem'], 44 | sorted(obj.keys())) 45 | self.assertEqual(['itval', 'myid', 'myval', 'sdef'], 46 | sorted(obj.values())) 47 | 48 | # Illegal operations 49 | self.assertRaises(ValueError, setattr, obj, 'foo', '') 50 | self.assertRaises(TypeError, obj.__delitem__, 'myattr') 51 | 52 | def test_refresh_from(self): 53 | obj = openpay.resource.BaseObject.construct_from({ 54 | 'foo': 'bar', 55 | 'trans': 'me', 56 | }, 'mykey') 57 | 58 | self.assertEqual('mykey', obj.api_key) 59 | self.assertEqual('bar', obj.foo) 60 | self.assertEqual('me', obj['trans']) 61 | 62 | obj.refresh_from({ 63 | 'foo': 'baz', 64 | 'johnny': 5, 65 | }, 'key2') 66 | 67 | self.assertEqual(5, obj.johnny) 68 | self.assertEqual('baz', obj.foo) 69 | self.assertRaises(AttributeError, getattr, obj, 'trans') 70 | self.assertEqual('key2', obj.api_key) 71 | 72 | obj.refresh_from({ 73 | 'trans': 4, 74 | 'metadata': {'amount': 42} 75 | }, 'key2', True) 76 | 77 | self.assertEqual('baz', obj.foo) 78 | self.assertEqual(4, obj.trans) 79 | self.assertEqual({'amount': 42}, obj._previous_metadata) 80 | 81 | # def test_refresh_from_nested_object(self): 82 | # obj = openpay.resource.BaseObject.construct_from( 83 | # SAMPLE_INVOICE, 'key') 84 | # 85 | # self.assertEqual(1, len(obj.lines.subscriptions)) 86 | # self.assertTrue( 87 | # isinstance(obj.lines.subscriptions[0], 88 | # openpay.resource.BaseObject)) 89 | # self.assertEqual('month', obj.lines.subscriptions[0].plan.interval) 90 | 91 | # def test_to_json(self): 92 | # obj = openpay.resource.BaseObject.construct_from( 93 | # SAMPLE_INVOICE, 'key') 94 | # 95 | # self.check_invoice_data(json.loads(str(obj))) 96 | 97 | def check_invoice_data(self, data): 98 | # Check rough structure 99 | self.assertEqual(20, len(list(data.keys()))) 100 | self.assertEqual(3, len(list(data['lines'].keys()))) 101 | self.assertEqual(0, len(data['lines']['invoiceitems'])) 102 | self.assertEqual(1, len(data['lines']['subscriptions'])) 103 | 104 | # Check various data types 105 | self.assertEqual(1338238728, data['date']) 106 | self.assertEqual(None, data['next_payment_attempt']) 107 | self.assertEqual(False, data['livemode']) 108 | self.assertEqual('month', 109 | data['lines']['subscriptions'][0]['plan']['interval']) 110 | 111 | 112 | class ListObjectTests(OpenpayApiTestCase): 113 | 114 | def setUp(self): 115 | super(ListObjectTests, self).setUp() 116 | 117 | self.lo = openpay.resource.ListObject.construct_from({ 118 | 'id': 'me', 119 | 'url': '/my/path', 120 | 'item_type': 'charge' 121 | }, 'mykey') 122 | 123 | self.mock_response([{ 124 | 'foo': 'bar', 125 | }]) 126 | 127 | def assertResponse(self, res): 128 | self.assertTrue(isinstance(res.data[0], openpay.Charge)) 129 | self.assertEqual('bar', res.data[0].foo) 130 | 131 | def test_all(self): 132 | res = self.lo.all(myparam='you') 133 | 134 | self.requestor_mock.request.assert_called_with( 135 | 'get', '/my/path', {'myparam': 'you'}) 136 | 137 | self.assertResponse(res) 138 | 139 | def test_create(self): 140 | res = self.lo.create(myparam='eter') 141 | 142 | self.requestor_mock.request.assert_called_with( 143 | 'post', '/my/path', {'myparam': 'eter'}) 144 | 145 | self.assertResponse(res) 146 | 147 | def test_retrieve(self): 148 | res = self.lo.retrieve('myid', myparam='cow') 149 | 150 | self.requestor_mock.request.assert_called_with( 151 | 'get', '/my/path/myid', {'myparam': 'cow'}) 152 | 153 | self.assertResponse(res) 154 | 155 | 156 | class APIResourceTests(OpenpayApiTestCase): 157 | 158 | def test_retrieve_and_refresh(self): 159 | self.mock_response({ 160 | 'id': 'foo2', 161 | 'bobble': 'scrobble', 162 | }) 163 | 164 | res = MyResource.retrieve('foo*', myparam=5) 165 | 166 | url = '/v1/{0}/myresources/foo%2A'.format(openpay.merchant_id) 167 | self.requestor_mock.request.assert_called_with( 168 | 'get', url, {'myparam': 5} 169 | ) 170 | 171 | self.assertEqual('scrobble', res.bobble) 172 | self.assertEqual('foo2', res.id) 173 | self.assertEqual('reskey', res.api_key) 174 | 175 | self.mock_response({ 176 | 'frobble': 5, 177 | }) 178 | 179 | res = res.refresh() 180 | 181 | url = '/v1/{0}/myresources/foo2'.format(openpay.merchant_id) 182 | self.requestor_mock.request.assert_called_with( 183 | 'get', url, {'myparam': 5} 184 | ) 185 | 186 | self.assertEqual(5, res.frobble) 187 | self.assertRaises(KeyError, res.__getitem__, 'bobble') 188 | 189 | def test_convert_to_openpay_object(self): 190 | sample = { 191 | 'foo': 'bar', 192 | 'adict': { 193 | 'object': 'charge', 194 | 'id': 42, 195 | 'amount': 7, 196 | }, 197 | 'alist': [ 198 | { 199 | 'object': 'customer', 200 | 'name': 'chilango' 201 | } 202 | ] 203 | } 204 | 205 | converted = openpay.resource.convert_to_openpay_object(sample, 'akey') 206 | 207 | # Types 208 | self.assertTrue(isinstance(converted, openpay.resource.BaseObject)) 209 | self.assertTrue(isinstance(converted.adict, openpay.Charge)) 210 | self.assertEqual(1, len(converted.alist)) 211 | self.assertTrue(isinstance(converted.alist[0], openpay.Customer)) 212 | 213 | # Values 214 | self.assertEqual('bar', converted.foo) 215 | self.assertEqual(42, converted.adict.id) 216 | self.assertEqual('chilango', converted.alist[0].name) 217 | 218 | # Stripping 219 | # TODO: We should probably be stripping out this property 220 | # self.assertRaises(AttributeError, getattr, converted.adict, 'object') 221 | 222 | 223 | class SingletonAPIResourceTests(OpenpayApiTestCase): 224 | 225 | def test_retrieve(self): 226 | self.mock_response({ 227 | 'single': 'ton' 228 | }) 229 | res = MySingleton.retrieve() 230 | url = '/v1/{0}/mysingleton'.format(openpay.merchant_id) 231 | self.requestor_mock.request.assert_called_with( 232 | 'get', url, {}) 233 | 234 | self.assertEqual('ton', res.single) 235 | 236 | 237 | class ListableAPIResourceTests(OpenpayApiTestCase): 238 | 239 | def test_all(self): 240 | self.mock_response([ 241 | { 242 | 'object': 'charge', 243 | 'name': 'jose', 244 | }, 245 | { 246 | 'object': 'charge', 247 | 'name': 'curly', 248 | } 249 | ]) 250 | 251 | res = MyListable.all() 252 | url = '/v1/{0}/mylistables'.format(openpay.merchant_id) 253 | self.requestor_mock.request.assert_called_with( 254 | 'get', url, {}) 255 | 256 | self.assertEqual(2, len(res.data)) 257 | self.assertTrue(all( 258 | isinstance(obj, openpay.Charge) for obj in res.data)) 259 | self.assertEqual('jose', res.data[0].name) 260 | self.assertEqual('curly', res.data[1].name) 261 | 262 | 263 | class CreateableAPIResourceTests(OpenpayApiTestCase): 264 | 265 | def test_create(self): 266 | self.mock_response({ 267 | 'object': 'charge', 268 | 'foo': 'bar', 269 | }) 270 | 271 | res = MyCreatable.create() 272 | url = '/v1/{0}/mycreatables'.format(openpay.merchant_id) 273 | self.requestor_mock.request.assert_called_with( 274 | 'post', url, {}) 275 | 276 | self.assertTrue(isinstance(res, openpay.Charge)) 277 | self.assertEqual('bar', res.foo) 278 | 279 | 280 | class UpdateableAPIResourceTests(OpenpayApiTestCase): 281 | 282 | def setUp(self): 283 | super(UpdateableAPIResourceTests, self).setUp() 284 | 285 | self.mock_response({ 286 | 'thats': 'it' 287 | }) 288 | 289 | self.obj = MyUpdateable.construct_from({ 290 | 'id': 'myid', 291 | 'foo': 'bar', 292 | 'baz': 'boz', 293 | 'metadata': { 294 | 'size': 'l', 295 | 'score': 4, 296 | 'height': 10 297 | } 298 | }, 'mykey') 299 | 300 | def checkSave(self): 301 | self.assertTrue(self.obj is self.obj.save()) 302 | 303 | self.assertEqual('it', self.obj.thats) 304 | # TODO: Should we force id to be retained? 305 | # self.assertEqual('myid', obj.id) 306 | self.assertRaises(AttributeError, getattr, self.obj, 'baz') 307 | 308 | def test_save(self): 309 | self.obj.baz = 'updated' 310 | self.obj.other = 'newval' 311 | self.obj.metadata.size = 'm' 312 | self.obj.metadata.info = 'a2' 313 | self.obj.metadata.height = None 314 | 315 | self.checkSave() 316 | 317 | self.requestor_mock.request.assert_called_with( 318 | 'put', 319 | '/v1/{0}/myupdateables/myid'.format(openpay.merchant_id), 320 | MyUpdateable.construct_from({ 321 | 'id': 'myid', 322 | 'foo': 'bar', 323 | 'baz': 'updated', 324 | 'other': 'newval', 325 | 'status': None, 326 | 'metadata': { 327 | 'size': 'm', 328 | 'info': 'a2', 329 | 'height': None, 330 | 'score': 4 331 | } 332 | }, 'mykey') 333 | ) 334 | 335 | def test_save_replace_metadata(self): 336 | self.obj.baz = 'updated' 337 | self.obj.other = 'newval' 338 | self.obj.metadata = { 339 | 'size': 'm', 340 | 'info': 'a2', 341 | 'score': 4, 342 | } 343 | 344 | self.checkSave() 345 | 346 | self.requestor_mock.request.assert_called_with( 347 | 'put', 348 | '/v1/{0}/myupdateables/myid'.format(openpay.merchant_id), 349 | MyUpdateable.construct_from({ 350 | 'baz': 'updated', 351 | 'other': 'newval', 352 | 'id': 'myid', 353 | 'foo': 'bar', 354 | 'status': None, 355 | 'metadata': { 356 | 'size': 'm', 357 | 'info': 'a2', 358 | 'height': '', 359 | 'score': 4, 360 | } 361 | }, 'mykey') 362 | ) 363 | 364 | 365 | class DeletableAPIResourceTests(OpenpayApiTestCase): 366 | 367 | def test_delete(self): 368 | self.mock_response({ 369 | 'id': 'mid', 370 | 'deleted': True, 371 | }) 372 | 373 | obj = MyDeletable.construct_from({ 374 | 'id': 'mid' 375 | }, 'mykey') 376 | 377 | self.assertTrue(obj is obj.delete()) 378 | 379 | self.assertEqual(True, obj.deleted) 380 | self.assertEqual('mid', obj.id) 381 | -------------------------------------------------------------------------------- /openpay/testPe/__init__.py: -------------------------------------------------------------------------------- 1 | import pkgutil 2 | import unittest 3 | 4 | 5 | def all_names(): 6 | for _, modname, _ in pkgutil.iter_modules(__path__): 7 | if modname.startswith('test_'): 8 | yield 'openpay.testPe.' + modname 9 | 10 | 11 | def all(): 12 | return unittest.defaultTestLoader.loadTestsFromNames(all_names()) 13 | 14 | 15 | def unit(): 16 | unit_names = [name for name in all_names() if 'integration' not in name] 17 | return unittest.defaultTestLoader.loadTestsFromNames(unit_names) 18 | -------------------------------------------------------------------------------- /openpay/testPe/helperpe.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | import datetime 5 | import os 6 | import random 7 | import re 8 | import sys 9 | import time 10 | import unittest 11 | 12 | from future.builtins import str 13 | from future.builtins import super 14 | from mock import patch, Mock 15 | 16 | import openpay 17 | 18 | 19 | def generate_order_id(): 20 | order_id = 'oid-test-{0}-{1}'.format( 21 | random.randint(1, 3000), str(time.time())[7:]) 22 | if len(order_id) > 20: 23 | order_id = order_id[:20] 24 | 25 | return order_id 26 | 27 | 28 | NOW = datetime.datetime.now() 29 | DUMMY_CUSTOMER = { 30 | "name": "Cliente Peru", 31 | "last_name": "Vazquez Juarez", 32 | "phone_number": "4448936475", 33 | "email": "juan.vazquez@empresa.pe" 34 | } 35 | DUMMY_CARD = { 36 | 'holder_name': 'Juan Lopez', 37 | 'card_number': '4111111111111111', 38 | "cvv2": "110", 39 | 'expiration_month': NOW.month, 40 | 'expiration_year': str(NOW.year + 4)[2:] 41 | } 42 | 43 | DUMMY_CHARGE = { 44 | 'method': 'card', 45 | 'amount': 100, 46 | 'currency': 'PEN', 47 | 'source_id': '', 48 | 'description': 'Dummy Charge', 49 | 'device_session_id': '' 50 | } 51 | 52 | DUMMY_CHARGE_MERCHANT = { 53 | 'method': 'card', 54 | 'amount': 100, 55 | 'currency': 'PEN', 56 | 'description': 'Dummy Charge', 57 | 'confirm': 'false', 58 | 'redirect_url': 'http://testing-openpay-pe.com', 59 | 'customer': DUMMY_CUSTOMER 60 | } 61 | 62 | DUMMY_CHARGE_STORE = { 63 | 'method': 'store', 64 | 'amount': 100, 65 | 'currency': 'PEN', 66 | 'description': 'Store Charge' 67 | } 68 | 69 | DUMMY_CHARGE_STORE_MERCHANT = { 70 | 'method': 'store', 71 | 'amount': 100, 72 | 'currency': 'PEN', 73 | 'description': 'Store Charge', 74 | 'customer': DUMMY_CUSTOMER 75 | } 76 | 77 | DUMMY_PLAN = { 78 | 'name': 'Amazing Gold Plan', 79 | 'amount': 2000, 80 | 'repeat_every': 1, 81 | 'repeat_unit': 'month', 82 | 'retry_times': 2, 83 | 'status_after_retry': 'cancelled', 84 | 'trial_days': 0 85 | } 86 | 87 | DUMMY_TRANSFER = { 88 | 'amount': 400, 89 | 'customer_id': 'acuqxruyv0hi1wfdwmym', 90 | 'description': 'Dummy Transfer', 91 | 'order_id': 'oid-00099', 92 | } 93 | 94 | DUMMY_ADDRESS = { 95 | "city": "Lima", 96 | "country_code": "PE", 97 | "postal_code": "02002", 98 | "line1": "Av 5 de Febrero", 99 | "line2": "Roble 207", 100 | "line3": "col carrillo", 101 | "state": "Lima" 102 | } 103 | 104 | DUMMY_CHECKOUT = { 105 | "amount": 250, 106 | "currency": "PEN", 107 | "description": "Cargo cobro con link", 108 | "redirect_url": "https://misitioempresa.pe", 109 | "order_id": "oid-12331", 110 | "expiration_date": "2021-08-31 12:50", 111 | "send_email": True, 112 | "customer": DUMMY_CUSTOMER 113 | } 114 | 115 | DUMMY_CHECKOUT_WITHOUT_CUSTOMER = { 116 | "amount": 250, 117 | "currency": "PEN", 118 | "description": "Cargo cobro con link", 119 | "redirect_url": "https://misitioempresa.pe", 120 | "order_id": "oid-12331", 121 | "expiration_date": "2021-08-31 12:50", 122 | "send_email": True 123 | } 124 | 125 | DUMMY_WEBHOOK = { 126 | "url": "https://webhook.site/99e29beb-fd9e-4c3f-9b49-ad28bac3daa5", 127 | "user": "juanito", 128 | "password": "passjuanito", 129 | "event_types": 130 | [ 131 | "charge.refunded", 132 | "charge.failed", 133 | "charge.cancelled", 134 | "charge.created", 135 | "chargeback.accepted" 136 | ] 137 | } 138 | 139 | DUMMY_TOKEN = { 140 | "card_number": "4111111111111111", 141 | "holder_name": "Juan Perez Ramirez", 142 | "expiration_year": "21", 143 | "expiration_month": "12", 144 | "cvv2": "110", 145 | "address": { 146 | "city": "Lima", 147 | "country_code": "PE", 148 | "postal_code": "110511", 149 | "line1": "Av 5 de Febrero", 150 | "line2": "Roble 207", 151 | "line3": "col carrillo", 152 | "state": "Lima" 153 | } 154 | } 155 | 156 | class OpenpayTestCase(unittest.TestCase): 157 | RESTORE_ATTRIBUTES = ('api_version', 'api_key') 158 | 159 | def setUp(self): 160 | super(OpenpayTestCase, self).setUp() 161 | 162 | self._openpay_original_attributes = {} 163 | 164 | for attr in self.RESTORE_ATTRIBUTES: 165 | self._openpay_original_attributes[attr] = getattr(openpay, attr) 166 | 167 | api_base = os.environ.get('OPENPAY_API_BASE') 168 | if api_base: 169 | openpay.api_base = api_base 170 | # Sandbox 171 | openpay.api_key = os.environ.get( 172 | 'OPENPAY_API_KEY', 'sk_f934dfe51645483e82106301d985a4f6') 173 | openpay.merchant_id = "m3cji4ughukthjcsglv0" 174 | openpay.country = "pe" 175 | # Dev 176 | # openpay.api_key = os.environ.get( 177 | # 'OPENPAY_API_KEY', '68df281c16184d47bb773d70abd4191b') 178 | # openpay.merchant_id = "m4se8bd4fef1mkzk6d1b" 179 | openpay.verify_ssl_certs = False 180 | 181 | def tearDown(self): 182 | super(OpenpayTestCase, self).tearDown() 183 | 184 | for attr in self.RESTORE_ATTRIBUTES: 185 | setattr(openpay, attr, self._openpay_original_attributes[attr]) 186 | 187 | # Python < 2.7 compatibility 188 | def assertRaisesRegexp(self, exception, regexp, callable, *args, **kwargs): 189 | try: 190 | callable(*args, **kwargs) 191 | except exception as err: 192 | if regexp is None: 193 | return True 194 | 195 | if isinstance(regexp, str): 196 | regexp = re.compile(regexp) 197 | if not regexp.search(str(err)): 198 | raise self.failureException('"%s" does not match "%s"' % 199 | (regexp.pattern, str(err))) 200 | else: 201 | raise self.failureException( 202 | '%s was not raised' % (exception.__name__,)) 203 | 204 | 205 | class OpenpayUnitTestCase(OpenpayTestCase): 206 | REQUEST_LIBRARIES = ['urlfetch', 'requests', 'pycurl'] 207 | 208 | if sys.version_info >= (3, 0): 209 | REQUEST_LIBRARIES.append('urllib.request') 210 | else: 211 | REQUEST_LIBRARIES.append('urllib2') 212 | 213 | def setUp(self): 214 | super(OpenpayUnitTestCase, self).setUp() 215 | 216 | self.request_patchers = {} 217 | self.request_mocks = {} 218 | for lib in self.REQUEST_LIBRARIES: 219 | patcher = patch("openpay.http_client.%s" % (lib,)) 220 | 221 | self.request_mocks[lib] = patcher.start() 222 | self.request_patchers[lib] = patcher 223 | 224 | def tearDown(self): 225 | super(OpenpayUnitTestCase, self).tearDown() 226 | 227 | for patcher in list(self.request_patchers.values()): 228 | patcher.stop() 229 | 230 | 231 | class OpenpayApiTestCase(OpenpayTestCase): 232 | 233 | def setUp(self): 234 | super(OpenpayApiTestCase, self).setUp() 235 | 236 | self.requestor_patcher = patch('openpay.api.APIClient') 237 | requestor_class_mock = self.requestor_patcher.start() 238 | self.requestor_mock = requestor_class_mock.return_value 239 | 240 | def tearDown(self): 241 | super(OpenpayApiTestCase, self).tearDown() 242 | 243 | self.requestor_patcher.stop() 244 | 245 | def mock_response(self, res): 246 | self.requestor_mock.request = Mock(return_value=(res, 'reskey')) 247 | 248 | 249 | class MyResource(openpay.resource.APIResource): 250 | pass 251 | 252 | 253 | class MySingleton(openpay.resource.SingletonAPIResource): 254 | pass 255 | 256 | 257 | class MyListable(openpay.resource.ListableAPIResource): 258 | pass 259 | 260 | 261 | class MyCreatable(openpay.resource.CreateableAPIResource): 262 | pass 263 | 264 | 265 | class MyUpdateable(openpay.resource.UpdateableAPIResource): 266 | pass 267 | 268 | 269 | class MyDeletable(openpay.resource.DeletableAPIResource): 270 | pass 271 | 272 | 273 | class MyComposite(openpay.resource.ListableAPIResource, 274 | openpay.resource.CreateableAPIResource, 275 | openpay.resource.UpdateableAPIResource, 276 | openpay.resource.DeletableAPIResource): 277 | pass 278 | -------------------------------------------------------------------------------- /openpay/testPe/test_pe_integration.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from random import randint 5 | 6 | from datetime import datetime, timedelta 7 | import os 8 | import sys 9 | import time 10 | import unittest 11 | 12 | from future.builtins import int 13 | from future.builtins import str 14 | from future.builtins import super 15 | from future.builtins import zip 16 | from mock import patch 17 | 18 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..')) 19 | import openpay 20 | 21 | from openpay.testPe.helperpe import ( 22 | OpenpayTestCase, 23 | NOW, DUMMY_CARD, DUMMY_CHARGE, DUMMY_PLAN, 24 | DUMMY_CHARGE_STORE, generate_order_id, DUMMY_CUSTOMER, DUMMY_ADDRESS, DUMMY_CHECKOUT, DUMMY_CHECKOUT_WITHOUT_CUSTOMER, 25 | DUMMY_CHARGE_MERCHANT, DUMMY_WEBHOOK, DUMMY_TOKEN) 26 | 27 | 28 | class FunctionalTests(OpenpayTestCase): 29 | request_client = openpay.http_client.Urllib2Client 30 | 31 | def setUp(self): 32 | super(FunctionalTests, self).setUp() 33 | 34 | def get_http_client(*args, **kwargs): 35 | return self.request_client(*args, **kwargs) 36 | 37 | self.client_patcher = patch( 38 | 'openpay.http_client.new_default_http_client') 39 | 40 | client_mock = self.client_patcher.start() 41 | client_mock.side_effect = get_http_client 42 | 43 | def tearDown(self): 44 | super(FunctionalTests, self).tearDown() 45 | 46 | self.client_patcher.stop() 47 | 48 | def test_dns_failure(self): 49 | self.patched_api_base = patch( 50 | 'openpay.get_api_base', 51 | lambda: 'https://my-invalid-domain.ireallywontresolve/v1') 52 | # get_api_base_mock = self.patched_api_base.start() 53 | self.patched_api_base.start() 54 | try: 55 | self.assertRaises(openpay.error.APIConnectionError, 56 | openpay.Customer.create) 57 | finally: 58 | self.patched_api_base.stop() 59 | 60 | def test_run(self): 61 | DUMMY_CHARGE['order_id'] = generate_order_id() 62 | charge = openpay.Charge.create(**DUMMY_CHARGE) 63 | # self.assertFalse(hasattr(charge, 'refund')) 64 | charge.refund(merchant=True) 65 | self.assertTrue(hasattr(charge, 'refund')) 66 | 67 | def test_refresh(self): 68 | DUMMY_CHARGE['order_id'] = generate_order_id() 69 | charge = openpay.Charge.create(**DUMMY_CHARGE) 70 | charge2 = openpay.Charge.retrieve_as_merchant(charge.id) 71 | self.assertEqual(charge2.creation_date, charge.creation_date) 72 | 73 | charge2.junk = 'junk' 74 | charge2.refresh() 75 | self.assertRaises(AttributeError, lambda: charge2.junk) 76 | 77 | def test_list_accessors(self): 78 | customer = openpay.Customer.create( 79 | name="Miguel Lopez", email="mlopez@example.com") 80 | self.assertEqual(customer['creation_date'], customer.creation_date) 81 | customer['foo'] = 'bar' 82 | self.assertEqual(customer.foo, 'bar') 83 | 84 | def test_raise(self): 85 | EXPIRED_CARD = DUMMY_CARD.copy() 86 | expiration_year = NOW.year - 2 87 | EXPIRED_CARD['expiration_month'] = NOW.month 88 | EXPIRED_CARD['expiration_year'] = str(expiration_year)[2:] 89 | self.assertRaises(openpay.error.InvalidRequestError, openpay.Charge.create, 90 | amount=100, method='card', description="Test Order", 91 | order_id="oid-00080", card=EXPIRED_CARD, currency="PEN") 92 | 93 | def test_unicode(self): 94 | # Make sure unicode requests can be sent 95 | self.assertRaises(openpay.error.InvalidRequestError, 96 | openpay.Charge.retrieve_as_merchant, 97 | id=u'☃') 98 | 99 | # def test_none_values(self): 100 | # customer = openpay.Customer.create(name=None, last_name=None) 101 | # self.assertTrue(customer.id) 102 | 103 | def test_missing_id(self): 104 | customer = openpay.Customer() 105 | self.assertRaises(openpay.error.InvalidRequestError, customer.refresh) 106 | 107 | 108 | class RequestsFunctionalTests(FunctionalTests): 109 | request_client = openpay.http_client.RequestsClient 110 | 111 | 112 | # Avoid skipTest errors in < 2.7 113 | if sys.version_info >= (2, 7): 114 | class UrlfetchFunctionalTests(FunctionalTests): 115 | request_client = 'urlfetch' 116 | 117 | def setUp(self): 118 | if openpay.http_client.urlfetch is None: 119 | self.skipTest( 120 | '`urlfetch` from Google App Engine is unavailable.') 121 | else: 122 | super(UrlfetchFunctionalTests, self).setUp() 123 | 124 | 125 | # class PycurlFunctionalTests(FunctionalTests): 126 | # def setUp(self): 127 | # if sys.version_info >= (3, 0): 128 | # self.skipTest('Pycurl is not supported in Python 3') 129 | # else: 130 | # super(PycurlFunctionalTests, self).setUp() 131 | 132 | # request_client = openpay.http_client.PycurlClient 133 | 134 | 135 | class AuthenticationErrorTest(OpenpayTestCase): 136 | 137 | def test_invalid_credentials(self): 138 | key = openpay.api_key 139 | try: 140 | openpay.api_key = 'invalid' 141 | openpay.Customer.create() 142 | except openpay.error.AuthenticationError as e: 143 | self.assertEqual(401, e.http_status) 144 | self.assertTrue(isinstance(e.http_body, str)) 145 | self.assertTrue(isinstance(e.json_body, dict)) 146 | finally: 147 | openpay.api_key = key 148 | 149 | 150 | class CardErrorTest(OpenpayTestCase): 151 | 152 | def test_declined_card_props(self): 153 | EXPIRED_CARD = DUMMY_CARD.copy() 154 | expiration_year = NOW.year - 2 155 | EXPIRED_CARD['expiration_month'] = NOW.month 156 | EXPIRED_CARD['expiration_year'] = str(expiration_year)[2:] 157 | try: 158 | openpay.Charge.create(amount=100, 159 | method='card', 160 | description="Test Order", 161 | order_id="oid-00080", 162 | currency="PEN", 163 | card=EXPIRED_CARD) 164 | except openpay.error.InvalidRequestError as e: 165 | self.assertEqual(400, e.http_status) 166 | self.assertTrue(isinstance(e.http_body, str)) 167 | self.assertTrue(isinstance(e.json_body, dict)) 168 | 169 | 170 | # Note that these are in addition to the core functional charge tests 171 | # OK 172 | class ChargeTest(OpenpayTestCase): 173 | 174 | def setUp(self): 175 | super(ChargeTest, self).setUp() 176 | 177 | def test_charge_list_all(self): 178 | charge_list = openpay.Charge.all( 179 | creation={'lte': NOW.strftime("%Y-%m-%d")}) 180 | list_result = charge_list.all( 181 | creation={'lte': NOW.strftime("%Y-%m-%d")}) 182 | 183 | self.assertEqual(len(charge_list.data), 184 | len(list_result.data)) 185 | 186 | for expected, actual in zip(charge_list.data, 187 | list_result.data): 188 | self.assertEqual(expected.id, actual.id) 189 | 190 | def test_charge_with_token(self): 191 | token = openpay.Token.create(card_number='4111111111111111', holder_name='Juan Perez Ramirez', 192 | expiration_year='28', expiration_month='12', cvv2='110', address=DUMMY_ADDRESS) 193 | charge_list = openpay.Charge.all() 194 | DUMMY_CHARGE['device_session_id'] = generate_order_id() 195 | DUMMY_CHARGE['source_id'] = token.id 196 | DUMMY_CHARGE['customer'] = DUMMY_CUSTOMER 197 | charge = charge_list.create(**DUMMY_CHARGE) 198 | self.assertTrue(isinstance(charge, openpay.Charge)) 199 | self.assertEqual(DUMMY_CHARGE['amount'], charge.amount) 200 | 201 | def test_charge_with_token_and_customer(self): 202 | customer = openpay.Customer.create(name="Miguel Lopez", email="mlopez@example.com") 203 | token = openpay.Token.create(card_number='4111111111111111', 204 | holder_name='Juan Perez Ramirez', 205 | expiration_year='28', expiration_month='12', cvv2='110', address=DUMMY_ADDRESS) 206 | DUMMY_CHARGE['device_session_id'] = generate_order_id() 207 | DUMMY_CHARGE['source_id'] = token.id 208 | DUMMY_CHARGE['customer'] = customer.id 209 | charge = openpay.Charge.create(**DUMMY_CHARGE) 210 | self.assertTrue(isinstance(charge, openpay.Charge)) 211 | self.assertEqual(DUMMY_CHARGE['amount'], charge.amount) 212 | 213 | def test_charge_list_retrieve(self): 214 | charge_list = openpay.Charge.all() 215 | charge = charge_list.retrieve(charge_list.data[0].id) 216 | self.assertTrue(isinstance(charge, openpay.Charge)) 217 | 218 | def test_charge_store_as_customer(self): 219 | customer = openpay.Customer.create( 220 | name="Miguel Lopez", email="mlopez@example.com") 221 | charge = customer.charges.create(**DUMMY_CHARGE_STORE) 222 | self.assertTrue(hasattr(charge, 'payment_method')) 223 | self.assertTrue(hasattr(charge.payment_method, 'reference')) 224 | self.assertTrue(hasattr(charge.payment_method, 'barcode_url')) 225 | self.assertEqual( 226 | customer.charges.retrieve(charge.id).status, 227 | 'in_progress') 228 | 229 | def test_charge_store_as_merchant(self): 230 | DUMMY_CHARGE_STORE['customer'] = DUMMY_CUSTOMER 231 | charge = openpay.Charge.create_as_merchant(**DUMMY_CHARGE_STORE) 232 | self.assertTrue(hasattr(charge, 'payment_method')) 233 | self.assertTrue(hasattr(charge.payment_method, 'reference')) 234 | self.assertTrue(hasattr(charge.payment_method, 'barcode_url')) 235 | self.assertEqual(charge.payment_method.type, "store") 236 | self.assertTrue(isinstance(charge, openpay.Charge)) 237 | self.assertEqual( 238 | openpay.Charge.retrieve_as_merchant(charge.id).status, 239 | 'in_progress') 240 | 241 | class WebhookTest(OpenpayTestCase): 242 | def test_list_webhooks(self): 243 | webhooks = openpay.Webhook.all() 244 | print (webhooks) 245 | self.assertTrue(isinstance(webhooks.data, list)) 246 | 247 | def test_create_webhook(self): 248 | webhook = openpay.Webhook.create(**DUMMY_WEBHOOK) 249 | print (webhook) 250 | self.assertEqual(webhook.status, 'verified') 251 | self.assertEqual(webhook.user, DUMMY_WEBHOOK['user']) 252 | 253 | def test_retrieve_webhook(self): 254 | webhook_id = 'wa8i86qw6zpsgkcankbe' 255 | webhook = openpay.Webhook.retrieve(webhook_id) 256 | print webhook 257 | self.assertEqual(webhook.id, webhook_id) 258 | 259 | def test_delete_webhook(self): 260 | webhook = openpay.Webhook.create(**DUMMY_WEBHOOK) 261 | webhook.delete() 262 | self.assertEqual(webhook, {}) 263 | 264 | class CustomerTest(OpenpayTestCase): 265 | 266 | def test_list_customers(self): 267 | customers = openpay.Customer.all() 268 | print(customers) 269 | self.assertTrue(isinstance(customers.data, list)) 270 | 271 | def test_create_customer(self): 272 | name = "Miguel Lopez" 273 | customer = openpay.Customer.create( 274 | name=name, 275 | last_name="Mi last name", 276 | email="col@example.com", 277 | phone_number="5744484951", 278 | description="foo bar") 279 | self.assertEqual(name,customer['name']) 280 | 281 | def test_update_customer(self): 282 | name = "Miguel Lopez" 283 | newName = "Miguel Nevo Lopez" 284 | customer = openpay.Customer.create( 285 | name=name, 286 | last_name="Mi last name", 287 | email="col@example.com", 288 | phone_number="5744484951", 289 | description="foo bar") 290 | customer['name'] = newName 291 | customer.save() 292 | self.assertEqual(customer['name'], newName) 293 | 294 | def test_get_customer(self): 295 | customer = openpay.Customer.all().data[0] 296 | customer_ = openpay.Customer.retrieve(customer['id']) 297 | self.assertEqual(customer['id'], customer_['id']) 298 | 299 | def test_delete_customer(self): 300 | customer = openpay.Customer.create( 301 | name="Miguel Lopez", 302 | last_name="Mi last name", 303 | email="col@example.com", 304 | phone_number="5744484951", 305 | description="foo bar") 306 | openpay.Customer.delete(customer) 307 | self.assertEqual(customer, {}) 308 | 309 | def test_list_customer(self): 310 | customers = openpay.Customer.all() 311 | self.assertTrue(isinstance(customers['data'], list)) 312 | 313 | 314 | # ok 315 | class CustomerPlanTest(OpenpayTestCase): 316 | 317 | def setUp(self): 318 | super(CustomerPlanTest, self).setUp() 319 | try: 320 | self.plan_obj = openpay.Plan.create(**DUMMY_PLAN) 321 | except openpay.error.InvalidRequestError: 322 | self.plan_obj = None 323 | 324 | def tearDown(self): 325 | if self.plan_obj: 326 | try: 327 | self.plan_obj.delete() 328 | except openpay.error.InvalidRequestError: 329 | pass 330 | super(CustomerPlanTest, self).tearDown() 331 | 332 | def test_create_customer(self): 333 | self.assertRaises(openpay.error.InvalidRequestError, 334 | openpay.Customer.create, 335 | plan=None) 336 | customer = openpay.Customer.create( 337 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 338 | 339 | subscription = customer.subscriptions.create( 340 | plan_id=self.plan_obj.id, card=DUMMY_CARD) 341 | 342 | self.assertTrue(isinstance(subscription, openpay.Subscription)) 343 | subscription.delete() 344 | self.assertFalse(hasattr(customer, 'subscription')) 345 | self.assertFalse(hasattr(customer, 'plan')) 346 | 347 | def test_update_and_cancel_subscription(self): 348 | customer = openpay.Customer.create( 349 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 350 | 351 | sub = customer.subscriptions.create( 352 | plan_id=self.plan_obj.id, card=DUMMY_CARD) 353 | 354 | sub.cancel_at_period_end = True 355 | sub.save() 356 | self.assertEqual(sub.status, 'active') 357 | self.assertTrue(sub.cancel_at_period_end) 358 | sub.delete() 359 | 360 | def test_datetime_trial_end(self): 361 | trial_end = datetime.now() + timedelta(days=15) 362 | customer = openpay.Customer.create( 363 | name="Miguel", last_name="Lopez", email="mlopez@example.com") 364 | subscription = customer.subscriptions.create( 365 | plan_id=self.plan_obj.id, card=DUMMY_CARD, 366 | trial_end=trial_end.strftime('Y-m-d')) 367 | self.assertTrue(subscription.id) 368 | 369 | def test_integer_trial_end(self): 370 | trial_end_dttm = datetime.now() + timedelta(days=15) 371 | trial_end_int = int(time.mktime(trial_end_dttm.timetuple())) 372 | customer = openpay.Customer.create(name="Miguel", 373 | last_name="Lopez", 374 | email="mlopez@example.com") 375 | subscription = customer.subscriptions.create( 376 | plan_id=self.plan_obj.id, card=DUMMY_CARD, 377 | trial_end=trial_end_int) 378 | self.assertTrue(subscription.id) 379 | 380 | 381 | # ok 382 | class PlanTest(OpenpayTestCase): 383 | 384 | def setUp(self): 385 | super(PlanTest, self).setUp() 386 | 387 | def test_create_plan(self): 388 | self.assertRaises(openpay.error.InvalidRequestError, 389 | openpay.Plan.create, amount=250) 390 | p = openpay.Plan.create(**DUMMY_PLAN) 391 | self.assertTrue(hasattr(p, 'amount')) 392 | self.assertTrue(hasattr(p, 'id')) 393 | self.assertEqual(DUMMY_PLAN['amount'], p.amount) 394 | p.delete() 395 | self.assertEqual(list(p.keys()), []) 396 | # self.assertTrue(p.deleted) 397 | 398 | def test_update_plan(self): 399 | p = openpay.Plan.create(**DUMMY_PLAN) 400 | name = "New plan name" 401 | p.name = name 402 | p.save() 403 | self.assertEqual(name, p.name) 404 | p.delete() 405 | 406 | def test_update_plan_without_retrieving(self): 407 | p = openpay.Plan.create(**DUMMY_PLAN) 408 | 409 | name = 'updated plan name!' 410 | plan = openpay.Plan(p.id) 411 | plan.name = name 412 | 413 | # should only have name and id 414 | self.assertEqual(sorted(['id', 'name']), sorted(plan.keys())) 415 | plan.save() 416 | 417 | self.assertEqual(name, plan.name) 418 | # should load all the properties 419 | self.assertEqual(p.amount, plan.amount) 420 | p.delete() 421 | 422 | 423 | class CardTest(OpenpayTestCase): 424 | 425 | def setUp(self): 426 | super(CardTest, self).setUp() 427 | self.customer = openpay.Customer.create( 428 | name="John", last_name="Doe", description="Test User", 429 | email="johndoe@example.com") 430 | self.card = self.customer.cards.create( 431 | card_number="4111111111111111", 432 | holder_name="Juan Perez", 433 | expiration_year="25", 434 | expiration_month="12", 435 | cvv2="110", 436 | address={ 437 | "city": "Querétaro", 438 | "country_code": "MX", 439 | "postal_code": "76900", 440 | "line1": "Av 5 de Febrero", 441 | "line2": "Roble 207", 442 | "line3": "col carrillo", 443 | "state": "Queretaro" 444 | } 445 | ) 446 | 447 | def test_card_created(self): 448 | self.assertTrue(isinstance(self.card, openpay.Card)) 449 | 450 | def test_card_list_all(self): 451 | card_list = self.customer.cards.all() 452 | self.assertEqual(card_list.count, 1) 453 | self.assertEqual(len(card_list.data), card_list.count) 454 | self.assertTrue(isinstance(card_list, openpay.resource.ListObject)) 455 | 456 | def test_card_retrieve(self): 457 | card_list = self.customer.cards.all() 458 | card = card_list.data[0] 459 | retrieved_card = self.customer.cards.retrieve(card.id) 460 | self.assertEqual(card.id, retrieved_card.id) 461 | 462 | def test_card_zdelete(self): 463 | card_list = self.customer.cards.all() 464 | self.card = card_list.data[0] 465 | self.card.delete() 466 | self.assertEqual(list(self.card.keys()), []) 467 | 468 | @unittest.skip("Method not available") 469 | def test_card_update(self): 470 | card_list = self.customer.cards.all() 471 | card = card_list.data[0] 472 | card.holder_name = "Juan Hernandez" 473 | card.save() 474 | self.assertEqual(card.holder_name, "Juan Hernandez") 475 | 476 | class CheckoutTest(OpenpayTestCase): 477 | 478 | def order_id(self): 479 | return "oid_" + str(randint(11111, 99999)) 480 | 481 | def getFormatedDate(self, date): 482 | date = date.replace(" ", "T") 483 | return date + ":00.000-0500" 484 | 485 | def test_checkout_create_with_customer(self): 486 | order_id = self.order_id() 487 | DUMMY_CHECKOUT['order_id'] = order_id 488 | checkout = openpay.Checkout.create(**DUMMY_CHECKOUT) 489 | self.assertEqual(checkout.order_id, order_id) 490 | 491 | def test_checkout_create_without_customer(self): 492 | order_id = self.order_id() 493 | DUMMY_CHECKOUT_WITHOUT_CUSTOMER['order_id'] = order_id 494 | checkout = openpay.Checkout.create(customer_id="atm7ii76vionfzecvwcx",**DUMMY_CHECKOUT_WITHOUT_CUSTOMER) 495 | self.assertEqual(checkout.order_id, order_id) 496 | 497 | def test_checkout_list(self): 498 | checkouts = openpay.Checkout.all() 499 | self.assertEqual(len(checkouts.data), checkouts.count) 500 | self.assertTrue(isinstance(checkouts, openpay.resource.ListObject)) 501 | 502 | def test_checkout_update(self): 503 | expiration_date = '2021-10-15 23:36' 504 | checkouts = openpay.Checkout.all() 505 | checkout = checkouts.data[0] 506 | checkout.expiration_date = expiration_date 507 | checkout.save() 508 | print checkout.expiration_date 509 | print self.getFormatedDate(expiration_date) 510 | self.assertEqual(checkout.expiration_date, self.getFormatedDate(expiration_date)) 511 | 512 | def test_checkout_filtered_list(self): 513 | limit = 3 514 | startDate = "20211001" 515 | endDate = "20211011" 516 | checkouts = openpay.Checkout.all(limit=limit, startDate=startDate, endDate=endDate) 517 | self.assertEqual(len(checkouts.data), limit) 518 | self.assertTrue(isinstance(checkouts, openpay.resource.ListObject)) 519 | 520 | def test_get_checkout(self): 521 | limit = 3 522 | startDate = "20211001" 523 | endDate = "20211011" 524 | checkouts = openpay.Checkout.all(limit=limit, startDate=startDate, endDate=endDate) 525 | checkout = checkouts.data[0] 526 | my_checkout = openpay.Checkout.retrieve(checkout_id=checkout.id) 527 | self.assertEqual(my_checkout.id, checkout.id) 528 | 529 | @unittest.skip("Method not available") 530 | def test_checkout_get_by_order_id(self): 531 | my_checkout = openpay.Checkout.retrieve(order_id="oid_26851") 532 | print my_checkout 533 | 534 | class TokenTest(OpenpayTestCase): 535 | 536 | def test_create_token(self): 537 | token = openpay.Token.create(**DUMMY_TOKEN) 538 | self.assertTrue(isinstance(token, openpay.Token)) 539 | 540 | def test_get_token(self): 541 | token = openpay.Token.create(**DUMMY_TOKEN) 542 | token_ = openpay.Token.retrieve(token['id']) 543 | self.assertEqual(token_['id'], token['id']) 544 | 545 | if __name__ == '__main__': 546 | unittest.main() 547 | -------------------------------------------------------------------------------- /openpay/testPe/test_pe_resources.py: -------------------------------------------------------------------------------- 1 | from __future__ import print_function 2 | from __future__ import unicode_literals 3 | from future.builtins import super 4 | #import json 5 | import openpay 6 | #from openpay import util 7 | 8 | from openpay.test.helper import ( 9 | OpenpayUnitTestCase, OpenpayApiTestCase, 10 | MySingleton, MyListable, MyCreatable, MyUpdateable, MyDeletable, 11 | MyResource) 12 | 13 | 14 | class BaseObjectTests(OpenpayUnitTestCase): 15 | 16 | def test_initializes_with_parameters(self): 17 | obj = openpay.resource.BaseObject( 18 | 'foo', 'bar', myparam=5, yourparam='boo') 19 | 20 | self.assertEqual('foo', obj.id) 21 | self.assertEqual('bar', obj.api_key) 22 | 23 | def test_access(self): 24 | obj = openpay.resource.BaseObject('myid', 'mykey', myparam=5) 25 | 26 | # Empty 27 | self.assertRaises(AttributeError, getattr, obj, 'myattr') 28 | self.assertRaises(KeyError, obj.__getitem__, 'myattr') 29 | self.assertEqual('def', obj.get('myattr', 'def')) 30 | self.assertEqual(None, obj.get('myattr')) 31 | 32 | # Setters 33 | obj.myattr = 'myval' 34 | obj['myitem'] = 'itval' 35 | self.assertEqual('sdef', obj.setdefault('mydef', 'sdef')) 36 | 37 | # Getters 38 | self.assertEqual('myval', obj.setdefault('myattr', 'sdef')) 39 | self.assertEqual('myval', obj.myattr) 40 | self.assertEqual('myval', obj['myattr']) 41 | self.assertEqual('myval', obj.get('myattr')) 42 | 43 | self.assertEqual(['id', 'myattr', 'mydef', 'myitem'], 44 | sorted(obj.keys())) 45 | self.assertEqual(['itval', 'myid', 'myval', 'sdef'], 46 | sorted(obj.values())) 47 | 48 | # Illegal operations 49 | self.assertRaises(ValueError, setattr, obj, 'foo', '') 50 | self.assertRaises(TypeError, obj.__delitem__, 'myattr') 51 | 52 | def test_refresh_from(self): 53 | obj = openpay.resource.BaseObject.construct_from({ 54 | 'foo': 'bar', 55 | 'trans': 'me', 56 | }, 'mykey') 57 | 58 | self.assertEqual('mykey', obj.api_key) 59 | self.assertEqual('bar', obj.foo) 60 | self.assertEqual('me', obj['trans']) 61 | 62 | obj.refresh_from({ 63 | 'foo': 'baz', 64 | 'johnny': 5, 65 | }, 'key2') 66 | 67 | self.assertEqual(5, obj.johnny) 68 | self.assertEqual('baz', obj.foo) 69 | self.assertRaises(AttributeError, getattr, obj, 'trans') 70 | self.assertEqual('key2', obj.api_key) 71 | 72 | obj.refresh_from({ 73 | 'trans': 4, 74 | 'metadata': {'amount': 42} 75 | }, 'key2', True) 76 | 77 | self.assertEqual('baz', obj.foo) 78 | self.assertEqual(4, obj.trans) 79 | self.assertEqual({'amount': 42}, obj._previous_metadata) 80 | 81 | # def test_refresh_from_nested_object(self): 82 | # obj = openpay.resource.BaseObject.construct_from( 83 | # SAMPLE_INVOICE, 'key') 84 | # 85 | # self.assertEqual(1, len(obj.lines.subscriptions)) 86 | # self.assertTrue( 87 | # isinstance(obj.lines.subscriptions[0], 88 | # openpay.resource.BaseObject)) 89 | # self.assertEqual('month', obj.lines.subscriptions[0].plan.interval) 90 | 91 | # def test_to_json(self): 92 | # obj = openpay.resource.BaseObject.construct_from( 93 | # SAMPLE_INVOICE, 'key') 94 | # 95 | # self.check_invoice_data(json.loads(str(obj))) 96 | 97 | def check_invoice_data(self, data): 98 | # Check rough structure 99 | self.assertEqual(20, len(list(data.keys()))) 100 | self.assertEqual(3, len(list(data['lines'].keys()))) 101 | self.assertEqual(0, len(data['lines']['invoiceitems'])) 102 | self.assertEqual(1, len(data['lines']['subscriptions'])) 103 | 104 | # Check various data types 105 | self.assertEqual(1338238728, data['date']) 106 | self.assertEqual(None, data['next_payment_attempt']) 107 | self.assertEqual(False, data['livemode']) 108 | self.assertEqual('month', 109 | data['lines']['subscriptions'][0]['plan']['interval']) 110 | 111 | 112 | class ListObjectTests(OpenpayApiTestCase): 113 | 114 | def setUp(self): 115 | super(ListObjectTests, self).setUp() 116 | 117 | self.lo = openpay.resource.ListObject.construct_from({ 118 | 'id': 'me', 119 | 'url': '/my/path', 120 | 'item_type': 'charge' 121 | }, 'mykey') 122 | 123 | self.mock_response([{ 124 | 'foo': 'bar', 125 | }]) 126 | 127 | def assertResponse(self, res): 128 | self.assertTrue(isinstance(res.data[0], openpay.Charge)) 129 | self.assertEqual('bar', res.data[0].foo) 130 | 131 | def test_all(self): 132 | res = self.lo.all(myparam='you') 133 | 134 | self.requestor_mock.request.assert_called_with( 135 | 'get', '/my/path', {'myparam': 'you'}) 136 | 137 | self.assertResponse(res) 138 | 139 | def test_create(self): 140 | res = self.lo.create(myparam='eter') 141 | 142 | self.requestor_mock.request.assert_called_with( 143 | 'post', '/my/path', {'myparam': 'eter'}) 144 | 145 | self.assertResponse(res) 146 | 147 | def test_retrieve(self): 148 | res = self.lo.retrieve('myid', myparam='cow') 149 | 150 | self.requestor_mock.request.assert_called_with( 151 | 'get', '/my/path/myid', {'myparam': 'cow'}) 152 | 153 | self.assertResponse(res) 154 | 155 | 156 | class APIResourceTests(OpenpayApiTestCase): 157 | 158 | def test_retrieve_and_refresh(self): 159 | self.mock_response({ 160 | 'id': 'foo2', 161 | 'bobble': 'scrobble', 162 | }) 163 | 164 | res = MyResource.retrieve('foo*', myparam=5) 165 | 166 | url = '/v1/{0}/myresources/foo%2A'.format(openpay.merchant_id) 167 | self.requestor_mock.request.assert_called_with( 168 | 'get', url, {'myparam': 5} 169 | ) 170 | 171 | self.assertEqual('scrobble', res.bobble) 172 | self.assertEqual('foo2', res.id) 173 | self.assertEqual('reskey', res.api_key) 174 | 175 | self.mock_response({ 176 | 'frobble': 5, 177 | }) 178 | 179 | res = res.refresh() 180 | 181 | url = '/v1/{0}/myresources/foo2'.format(openpay.merchant_id) 182 | self.requestor_mock.request.assert_called_with( 183 | 'get', url, {'myparam': 5} 184 | ) 185 | 186 | self.assertEqual(5, res.frobble) 187 | self.assertRaises(KeyError, res.__getitem__, 'bobble') 188 | 189 | def test_convert_to_openpay_object(self): 190 | sample = { 191 | 'foo': 'bar', 192 | 'adict': { 193 | 'object': 'charge', 194 | 'id': 42, 195 | 'amount': 7, 196 | }, 197 | 'alist': [ 198 | { 199 | 'object': 'customer', 200 | 'name': 'chilango' 201 | } 202 | ] 203 | } 204 | 205 | converted = openpay.resource.convert_to_openpay_object(sample, 'akey') 206 | 207 | # Types 208 | self.assertTrue(isinstance(converted, openpay.resource.BaseObject)) 209 | self.assertTrue(isinstance(converted.adict, openpay.Charge)) 210 | self.assertEqual(1, len(converted.alist)) 211 | self.assertTrue(isinstance(converted.alist[0], openpay.Customer)) 212 | 213 | # Values 214 | self.assertEqual('bar', converted.foo) 215 | self.assertEqual(42, converted.adict.id) 216 | self.assertEqual('chilango', converted.alist[0].name) 217 | 218 | # Stripping 219 | # TODO: We should probably be stripping out this property 220 | # self.assertRaises(AttributeError, getattr, converted.adict, 'object') 221 | 222 | 223 | class SingletonAPIResourceTests(OpenpayApiTestCase): 224 | 225 | def test_retrieve(self): 226 | self.mock_response({ 227 | 'single': 'ton' 228 | }) 229 | res = MySingleton.retrieve() 230 | url = '/v1/{0}/mysingleton'.format(openpay.merchant_id) 231 | self.requestor_mock.request.assert_called_with( 232 | 'get', url, {}) 233 | 234 | self.assertEqual('ton', res.single) 235 | 236 | 237 | class ListableAPIResourceTests(OpenpayApiTestCase): 238 | 239 | def test_all(self): 240 | self.mock_response([ 241 | { 242 | 'object': 'charge', 243 | 'name': 'jose', 244 | }, 245 | { 246 | 'object': 'charge', 247 | 'name': 'curly', 248 | } 249 | ]) 250 | 251 | res = MyListable.all() 252 | url = '/v1/{0}/mylistables'.format(openpay.merchant_id) 253 | self.requestor_mock.request.assert_called_with( 254 | 'get', url, {}) 255 | 256 | self.assertEqual(2, len(res.data)) 257 | self.assertTrue(all( 258 | isinstance(obj, openpay.Charge) for obj in res.data)) 259 | self.assertEqual('jose', res.data[0].name) 260 | self.assertEqual('curly', res.data[1].name) 261 | 262 | 263 | class CreateableAPIResourceTests(OpenpayApiTestCase): 264 | 265 | def test_create(self): 266 | self.mock_response({ 267 | 'object': 'charge', 268 | 'foo': 'bar', 269 | }) 270 | 271 | res = MyCreatable.create() 272 | url = '/v1/{0}/mycreatables'.format(openpay.merchant_id) 273 | self.requestor_mock.request.assert_called_with( 274 | 'post', url, {}) 275 | 276 | self.assertTrue(isinstance(res, openpay.Charge)) 277 | self.assertEqual('bar', res.foo) 278 | 279 | 280 | class UpdateableAPIResourceTests(OpenpayApiTestCase): 281 | 282 | def setUp(self): 283 | super(UpdateableAPIResourceTests, self).setUp() 284 | 285 | self.mock_response({ 286 | 'thats': 'it' 287 | }) 288 | 289 | self.obj = MyUpdateable.construct_from({ 290 | 'id': 'myid', 291 | 'foo': 'bar', 292 | 'baz': 'boz', 293 | 'metadata': { 294 | 'size': 'l', 295 | 'score': 4, 296 | 'height': 10 297 | } 298 | }, 'mykey') 299 | 300 | def checkSave(self): 301 | self.assertTrue(self.obj is self.obj.save()) 302 | 303 | self.assertEqual('it', self.obj.thats) 304 | # TODO: Should we force id to be retained? 305 | # self.assertEqual('myid', obj.id) 306 | self.assertRaises(AttributeError, getattr, self.obj, 'baz') 307 | 308 | def test_save(self): 309 | self.obj.baz = 'updated' 310 | self.obj.other = 'newval' 311 | self.obj.metadata.size = 'm' 312 | self.obj.metadata.info = 'a2' 313 | self.obj.metadata.height = None 314 | 315 | self.checkSave() 316 | 317 | self.requestor_mock.request.assert_called_with( 318 | 'put', 319 | '/v1/{0}/myupdateables/myid'.format(openpay.merchant_id), 320 | MyUpdateable.construct_from({ 321 | 'id': 'myid', 322 | 'foo': 'bar', 323 | 'baz': 'updated', 324 | 'other': 'newval', 325 | 'status': None, 326 | 'metadata': { 327 | 'size': 'm', 328 | 'info': 'a2', 329 | 'height': None, 330 | 'score': 4 331 | } 332 | }, 'mykey') 333 | ) 334 | 335 | def test_save_replace_metadata(self): 336 | self.obj.baz = 'updated' 337 | self.obj.other = 'newval' 338 | self.obj.metadata = { 339 | 'size': 'm', 340 | 'info': 'a2', 341 | 'score': 4, 342 | } 343 | 344 | self.checkSave() 345 | 346 | self.requestor_mock.request.assert_called_with( 347 | 'put', 348 | '/v1/{0}/myupdateables/myid'.format(openpay.merchant_id), 349 | MyUpdateable.construct_from({ 350 | 'baz': 'updated', 351 | 'other': 'newval', 352 | 'id': 'myid', 353 | 'foo': 'bar', 354 | 'status': None, 355 | 'metadata': { 356 | 'size': 'm', 357 | 'info': 'a2', 358 | 'height': '', 359 | 'score': 4, 360 | } 361 | }, 'mykey') 362 | ) 363 | 364 | 365 | class DeletableAPIResourceTests(OpenpayApiTestCase): 366 | 367 | def test_delete(self): 368 | self.mock_response({ 369 | 'id': 'mid', 370 | 'deleted': True, 371 | }) 372 | 373 | obj = MyDeletable.construct_from({ 374 | 'id': 'mid' 375 | }, 'mykey') 376 | 377 | self.assertTrue(obj is obj.delete()) 378 | 379 | self.assertEqual(True, obj.deleted) 380 | self.assertEqual('mid', obj.id) 381 | -------------------------------------------------------------------------------- /openpay/util.py: -------------------------------------------------------------------------------- 1 | 2 | import logging 3 | import sys 4 | 5 | logger = logging.getLogger('stripe') 6 | 7 | __all__ = ['utf8'] 8 | 9 | 10 | def utf8(value): 11 | if isinstance(value, unicode) and sys.version_info < (3, 0): 12 | return value.encode('utf-8') 13 | else: 14 | return value 15 | -------------------------------------------------------------------------------- /openpay/version.py: -------------------------------------------------------------------------------- 1 | VERSION = '1.01' 2 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | import sys 3 | 4 | try: 5 | from setuptools import setup 6 | except ImportError: 7 | from distutils.core import setup 8 | 9 | try: 10 | from distutils.command.build_py import build_py_2to3 as build_py 11 | except ImportError: 12 | from distutils.command.build_py import build_py 13 | 14 | path, script = os.path.split(sys.argv[0]) 15 | os.chdir(os.path.abspath(path)) 16 | 17 | requests = 'requests >= 2.1.0' 18 | if sys.version_info < (2, 6): 19 | requests += ', < 2.1.0' 20 | install_requires = [requests, "future==0.15.2"] 21 | 22 | 23 | # Don't import openpay module here, since deps may not be installed 24 | sys.path.insert(0, os.path.join(os.path.dirname(__file__), 'openpay')) 25 | from version import VERSION 26 | 27 | # Get simplejson if we don't already have json 28 | if sys.version_info < (3, 0): 29 | try: 30 | import json 31 | except ImportError: 32 | install_requires.append('simplejson') 33 | 34 | setup(name='openpay', 35 | cmdclass={'build_py': build_py}, 36 | version=VERSION, 37 | description='Openpay python bindings', 38 | author='Openpay', 39 | author_email='soporte@openpay.mx', 40 | url='https://www.openpay.mx/', 41 | tests_require=['mock'], 42 | packages=['openpay', 'openpay.test'], 43 | package_data={'openpay': ['data/ca-certificates.crt', '../VERSION']}, 44 | install_requires=install_requires, 45 | test_suite='openpay.test.all', 46 | use_2to3=True, 47 | ) 48 | --------------------------------------------------------------------------------