├── .github └── workflows │ └── build-test-release.yml ├── .gitignore ├── .rspec ├── .rspec_status ├── .rubocop.yml ├── CHANGELOG.md ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Gemfile ├── LICENSE ├── README.md ├── Rakefile ├── bin ├── console └── setup ├── lib └── openpix │ ├── ruby_sdk.rb │ └── ruby_sdk │ ├── api_body_formatter.rb │ ├── api_response.rb │ ├── client.rb │ ├── http_client.rb │ ├── resources.rb │ ├── resources │ ├── charge.rb │ ├── customer.rb │ ├── payment.rb │ ├── refund.rb │ ├── resource.rb │ ├── subscription.rb │ └── webhook.rb │ ├── utils.rb │ └── version.rb ├── openpix-ruby_sdk.gemspec └── spec ├── openpix ├── ruby_sdk │ ├── api_response_spec.rb │ ├── client_spec.rb │ ├── http_client_spec.rb │ ├── resources │ │ ├── charge_spec.rb │ │ ├── customer_spec.rb │ │ ├── payment_spec.rb │ │ ├── refund_spec.rb │ │ ├── shared │ │ │ ├── destroyable_resource.rb │ │ │ ├── fetchable_resource.rb │ │ │ ├── findable_resource.rb │ │ │ ├── not_destroyable_resource.rb │ │ │ ├── not_fetchable_resource.rb │ │ │ ├── not_findable_resource.rb │ │ │ └── savable_resource.rb │ │ ├── subscription_spec.rb │ │ └── webhook_spec.rb │ └── utils_spec.rb └── ruby_sdk_spec.rb └── spec_helper.rb /.github/workflows/build-test-release.yml: -------------------------------------------------------------------------------- 1 | name: Build code, test it and create a release to a new version of this gem 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | permissions: 9 | contents: write 10 | pull-requests: write 11 | 12 | jobs: 13 | test: 14 | name: Test 15 | runs-on: ubuntu-latest 16 | timeout-minutes: 20 17 | strategy: 18 | matrix: 19 | ruby: [ '2.6', '2.7', '3.0', '3.1', '3.2', 'ruby-head' ] 20 | steps: 21 | - name: Checkout branch 22 | uses: actions/checkout@v3 23 | with: 24 | fetch-depth: 0 25 | - name: Set up Ruby 26 | uses: ruby/setup-ruby@v1 27 | with: 28 | ruby-version: ${{ matrix.ruby }} 29 | bundler-cache: true 30 | - name: Set up bundle 31 | run: bundle install --with development 32 | - name: Run linter 33 | run: bundle exec rubocop -d --cache true --parallel 34 | - name: Run Unit Tests 35 | run: bundle exec rspec 36 | 37 | release-please: 38 | runs-on: ubuntu-latest 39 | needs: [ test ] 40 | steps: 41 | - uses: google-github-actions/release-please-action@v3 42 | id: release 43 | with: 44 | release-type: ruby 45 | package-name: openpix-ruby_sdk 46 | version-file: "lib/openpix/ruby_sdk/version.rb" 47 | # Checkout code if release was created 48 | - uses: actions/checkout@v3 49 | if: ${{ steps.release.outputs.releases_created }} 50 | # Setup ruby if a release was created 51 | - uses: ruby/setup-ruby@v1 52 | with: 53 | ruby-version: 3.2.2 54 | if: ${{ steps.release.outputs.releases_created }} 55 | # Bundle install 56 | - run: bundle install 57 | if: ${{ steps.release.outputs.releases_created }} 58 | # Publish 59 | - name: publish gem 60 | run: | 61 | mkdir -p $HOME/.gem 62 | touch $HOME/.gem/credentials 63 | chmod 0600 $HOME/.gem/credentials 64 | printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials 65 | gem build *.gemspec 66 | gem push *.gem 67 | env: 68 | GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}" 69 | if: ${{ steps.release.outputs.releases_created }} 70 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /spec/examples.txt 9 | /test/tmp/ 10 | /test/version_tmp/ 11 | /tmp/ 12 | 13 | # Used by dotenv library to load environment variables. 14 | .env 15 | 16 | # Ignore Byebug command history file. 17 | .byebug_history 18 | 19 | ## Documentation cache and generated files: 20 | /.yardoc/ 21 | /_yardoc/ 22 | /doc/ 23 | /rdoc/ 24 | 25 | ## Environment normalization: 26 | /.bundle/ 27 | /vendor/bundle 28 | /lib/bundler/man/ 29 | Gemfile.lock 30 | .rspec_status 31 | 32 | # for a library or gem, you might want to ignore these files since the code is 33 | # intended to run in multiple environments; otherwise, check them in: 34 | .ruby-version 35 | .ruby-gemset 36 | 37 | # editor 38 | .vscode 39 | 40 | # system 41 | .DS_Store 42 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --require spec_helper 2 | -------------------------------------------------------------------------------- /.rspec_status: -------------------------------------------------------------------------------- 1 | example_id | status | run_time | 2 | ----------------------------------------------------------------- | ------ | --------------- | 3 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:1:1:1] | passed | 0.00004 seconds | 4 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:2:1:1] | passed | 0.00003 seconds | 5 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:3:1:1] | passed | 0.00004 seconds | 6 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:3:2:1] | passed | 0.00003 seconds | 7 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:4:1:1] | passed | 0.00004 seconds | 8 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:4:2:1] | passed | 0.00004 seconds | 9 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:4:3:1] | passed | 0.00003 seconds | 10 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:5:1:1] | passed | 0.00003 seconds | 11 | ./spec/openpix/ruby_sdk/api_response_spec.rb[1:5:2:1] | passed | 0.00003 seconds | 12 | ./spec/openpix/ruby_sdk/client_spec.rb[1:1:1] | passed | 0.00014 seconds | 13 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:1] | passed | 0.00007 seconds | 14 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:2] | passed | 0.00008 seconds | 15 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:3] | passed | 0.00009 seconds | 16 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:4] | passed | 0.00008 seconds | 17 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:5] | passed | 0.00009 seconds | 18 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:6] | passed | 0.00024 seconds | 19 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:7] | passed | 0.00009 seconds | 20 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:8] | passed | 0.00009 seconds | 21 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:9] | passed | 0.00009 seconds | 22 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:10] | passed | 0.0001 seconds | 23 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:11] | passed | 0.00008 seconds | 24 | ./spec/openpix/ruby_sdk/client_spec.rb[1:2:12] | passed | 0.00009 seconds | 25 | ./spec/openpix/ruby_sdk/http_client_spec.rb[1:1:1] | passed | 0.00003 seconds | 26 | ./spec/openpix/ruby_sdk/http_client_spec.rb[1:2:1] | passed | 0.00011 seconds | 27 | ./spec/openpix/ruby_sdk/http_client_spec.rb[1:2:2:1] | passed | 0.00008 seconds | 28 | ./spec/openpix/ruby_sdk/http_client_spec.rb[1:3:1] | passed | 0.00011 seconds | 29 | ./spec/openpix/ruby_sdk/http_client_spec.rb[1:4:1] | passed | 0.00013 seconds | 30 | ./spec/openpix/ruby_sdk/http_client_spec.rb[1:5:1] | passed | 0.0001 seconds | 31 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:1:1:1] | passed | 0.0001 seconds | 32 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:1:1:2:1] | passed | 0.0001 seconds | 33 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:1:2:1] | passed | 0.00016 seconds | 34 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:1:2:2:1] | passed | 0.00011 seconds | 35 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:1:1] | passed | 0.0001 seconds | 36 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:1:2:1] | passed | 0.00009 seconds | 37 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:2:1] | passed | 0.00011 seconds | 38 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:2:2:1] | passed | 0.0001 seconds | 39 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:3:1:1:1] | passed | 0.00016 seconds | 40 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:3:1:2:1] | passed | 0.0001 seconds | 41 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:3:2:1] | passed | 0.00004 seconds | 42 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:4:1:1:1] | passed | 0.00018 seconds | 43 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:4:1:2:1] | passed | 0.00011 seconds | 44 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:2:4:2:1] | passed | 0.00004 seconds | 45 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:3:1:1] | passed | 0.0001 seconds | 46 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:3:1:2:1] | passed | 0.00009 seconds | 47 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:3:2:1] | passed | 0.00008 seconds | 48 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:3:2:2:1] | passed | 0.00009 seconds | 49 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:4:1:1] | passed | 0.00008 seconds | 50 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:4:1:2:1] | passed | 0.00008 seconds | 51 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:4:2:1] | passed | 0.00009 seconds | 52 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:4:2:2:1] | passed | 0.00009 seconds | 53 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:5] | passed | 0.00003 seconds | 54 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:6] | passed | 0.00003 seconds | 55 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:7:1] | passed | 0.00004 seconds | 56 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:8:1:1] | passed | 0.00004 seconds | 57 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:8:2:1] | passed | 0.00005 seconds | 58 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:8:3:1:1] | passed | 0.00006 seconds | 59 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:8:3:2:1] | passed | 0.00005 seconds | 60 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:8:3:3:1] | passed | 0.00008 seconds | 61 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:9:1] | passed | 0.00056 seconds | 62 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:10:1] | passed | 0.00005 seconds | 63 | ./spec/openpix/ruby_sdk/resources/charge_spec.rb[1:11:1] | passed | 0.00004 seconds | 64 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:1:1:1] | passed | 0.0001 seconds | 65 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:1:1:2:1] | passed | 0.0001 seconds | 66 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:1:2:1] | passed | 0.0001 seconds | 67 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:1:2:2:1] | passed | 0.0001 seconds | 68 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:1:1] | passed | 0.00012 seconds | 69 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:1:2:1] | passed | 0.0001 seconds | 70 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:2:1] | passed | 0.00011 seconds | 71 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:2:2:1] | passed | 0.0001 seconds | 72 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:3:1:1:1] | passed | 0.00016 seconds | 73 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:3:1:2:1] | passed | 0.00011 seconds | 74 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:3:2:1] | passed | 0.00004 seconds | 75 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:4:1:1:1] | passed | 0.00017 seconds | 76 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:4:1:2:1] | passed | 0.0001 seconds | 77 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:2:4:2:1] | passed | 0.00005 seconds | 78 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:3:1:1] | passed | 0.00011 seconds | 79 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:3:1:2:1] | passed | 0.00009 seconds | 80 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:3:2:1] | passed | 0.00009 seconds | 81 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:3:2:2:1] | passed | 0.0001 seconds | 82 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:4:1:1] | passed | 0.00004 seconds | 83 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:4:2:1] | passed | 0.00005 seconds | 84 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:5] | passed | 0.00003 seconds | 85 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:6] | passed | 0.00003 seconds | 86 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:7:1] | passed | 0.00005 seconds | 87 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:8:1:1] | passed | 0.00004 seconds | 88 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:8:2:1] | passed | 0.00005 seconds | 89 | ./spec/openpix/ruby_sdk/resources/customer_spec.rb[1:8:3:1] | passed | 0.00005 seconds | 90 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:1:1:1] | passed | 0.00009 seconds | 91 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:1:1:2:1] | passed | 0.00009 seconds | 92 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:1:2:1] | passed | 0.0001 seconds | 93 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:1:2:2:1] | passed | 0.00009 seconds | 94 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:1:1] | passed | 0.0002 seconds | 95 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:1:2:1] | passed | 0.00009 seconds | 96 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:2:1] | passed | 0.00011 seconds | 97 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:2:2:1] | passed | 0.00009 seconds | 98 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:3:1:1:1] | passed | 0.00221 seconds | 99 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:3:1:2:1] | passed | 0.00012 seconds | 100 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:3:2:1] | passed | 0.00004 seconds | 101 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:4:1:1:1] | passed | 0.00017 seconds | 102 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:4:1:2:1] | passed | 0.00011 seconds | 103 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:2:4:2:1] | passed | 0.00004 seconds | 104 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:3:1:1] | passed | 0.00009 seconds | 105 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:3:1:2:1] | passed | 0.00009 seconds | 106 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:3:2:1] | passed | 0.0001 seconds | 107 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:3:2:2:1] | passed | 0.0001 seconds | 108 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:4:1:1] | passed | 0.00004 seconds | 109 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:4:2:1] | passed | 0.00003 seconds | 110 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:5] | passed | 0.00003 seconds | 111 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:6] | passed | 0.00003 seconds | 112 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:7:1] | passed | 0.00005 seconds | 113 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:8:1:1] | passed | 0.00004 seconds | 114 | ./spec/openpix/ruby_sdk/resources/payment_spec.rb[1:8:2:1] | passed | 0.00005 seconds | 115 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:1:1:1] | passed | 0.0001 seconds | 116 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:1:1:2:1] | passed | 0.0001 seconds | 117 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:1:2:1] | passed | 0.0001 seconds | 118 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:1:2:2:1] | passed | 0.0001 seconds | 119 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:1:1] | passed | 0.0001 seconds | 120 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:1:2:1] | passed | 0.00008 seconds | 121 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:2:1] | passed | 0.0001 seconds | 122 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:2:2:1] | passed | 0.00009 seconds | 123 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:3:1:1:1] | passed | 0.00016 seconds | 124 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:3:1:2:1] | passed | 0.00012 seconds | 125 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:3:2:1] | passed | 0.00004 seconds | 126 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:4:1:1:1] | passed | 0.00017 seconds | 127 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:4:1:2:1] | passed | 0.00011 seconds | 128 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:2:4:2:1] | passed | 0.00004 seconds | 129 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:3:1:1] | passed | 0.00009 seconds | 130 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:3:1:2:1] | passed | 0.00009 seconds | 131 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:3:2:1] | passed | 0.00009 seconds | 132 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:3:2:2:1] | passed | 0.00009 seconds | 133 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:4:1:1] | passed | 0.00003 seconds | 134 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:4:2:1] | passed | 0.00004 seconds | 135 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:5] | passed | 0.00003 seconds | 136 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:6] | passed | 0.00003 seconds | 137 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:7:1] | passed | 0.00004 seconds | 138 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:8:1:1] | passed | 0.00005 seconds | 139 | ./spec/openpix/ruby_sdk/resources/refund_spec.rb[1:8:2:1] | passed | 0.00005 seconds | 140 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:1:1:1] | passed | 0.0001 seconds | 141 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:1:1:2:1] | passed | 0.0001 seconds | 142 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:1:2:1] | passed | 0.0001 seconds | 143 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:1:2:2:1] | passed | 0.0001 seconds | 144 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:2:1:1] | passed | 0.0001 seconds | 145 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:2:1:2:1] | passed | 0.00008 seconds | 146 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:2:2:1] | passed | 0.00008 seconds | 147 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:2:2:2:1] | passed | 0.00009 seconds | 148 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:3:1:1] | passed | 0.00003 seconds | 149 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:3:2:1] | passed | 0.00003 seconds | 150 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:4:1:1] | passed | 0.00003 seconds | 151 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:4:2:1] | passed | 0.00003 seconds | 152 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:4:3:1] | passed | 0.00003 seconds | 153 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:4:4:1] | passed | 0.00003 seconds | 154 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:5] | passed | 0.00002 seconds | 155 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:6] | passed | 0.00003 seconds | 156 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:7:1] | passed | 0.00004 seconds | 157 | ./spec/openpix/ruby_sdk/resources/subscription_spec.rb[1:8:1] | passed | 0.00005 seconds | 158 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:1:1:1] | passed | 0.0001 seconds | 159 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:1:1:2:1] | passed | 0.0001 seconds | 160 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:1:2:1] | passed | 0.00012 seconds | 161 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:1:2:2:1] | passed | 0.00009 seconds | 162 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:1:1] | passed | 0.00011 seconds | 163 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:1:2:1] | passed | 0.00009 seconds | 164 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:2:1] | passed | 0.00011 seconds | 165 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:2:2:1] | passed | 0.0001 seconds | 166 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:3:1:1:1] | passed | 0.00016 seconds | 167 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:3:1:2:1] | passed | 0.0001 seconds | 168 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:3:2:1] | passed | 0.00004 seconds | 169 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:4:1:1:1] | passed | 0.0002 seconds | 170 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:4:1:2:1] | passed | 0.00011 seconds | 171 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:2:4:2:1] | passed | 0.00004 seconds | 172 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:3:1:1] | passed | 0.00421 seconds | 173 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:3:1:2:1] | passed | 0.00018 seconds | 174 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:3:2:1] | passed | 0.0001 seconds | 175 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:3:2:2:1] | passed | 0.00072 seconds | 176 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:4:1:1] | passed | 0.00004 seconds | 177 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:4:2:1] | passed | 0.0001 seconds | 178 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:5] | passed | 0.00004 seconds | 179 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:6] | passed | 0.00053 seconds | 180 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:7:1] | passed | 0.00004 seconds | 181 | ./spec/openpix/ruby_sdk/resources/webhook_spec.rb[1:8:1] | passed | 0.00004 seconds | 182 | ./spec/openpix/ruby_sdk/utils_spec.rb[1:1:1] | passed | 0.00071 seconds | 183 | ./spec/openpix/ruby_sdk/utils_spec.rb[1:1:2:1] | passed | 0.00017 seconds | 184 | ./spec/openpix/ruby_sdk_spec.rb[1:1] | passed | 0.00028 seconds | 185 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | TargetRubyVersion: 2.6 3 | NewCops: enable 4 | 5 | Naming/AccessorMethodName: 6 | Enabled: false 7 | 8 | Metrics/BlockLength: 9 | Enabled: false 10 | 11 | Metrics/MethodLength: 12 | Enabled: false 13 | 14 | Layout/LineLength: 15 | Enabled: false 16 | 17 | Metrics/AbcSize: 18 | Enabled: false 19 | 20 | Metrics/ClassLength: 21 | Enabled: false 22 | 23 | Metrics/CyclomaticComplexity: 24 | Enabled: false 25 | 26 | Metrics/PerceivedComplexity: 27 | Enabled: false 28 | 29 | Gemspec/DevelopmentDependencies: 30 | Enabled: false 31 | 32 | Gemspec/RequireMFA: 33 | Enabled: false 34 | 35 | Naming/VariableName: 36 | Enabled: false 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [Unreleased] 2 | 3 | ## [2.0.0](https://github.com/Open-Pix/ruby-sdk/compare/v1.2.0...v2.0.0) (2023-07-24) 4 | 5 | 6 | ### ⚠ BREAKING CHANGES 7 | 8 | * Refactor resources params to reflect better API payload structure ([#29](https://github.com/Open-Pix/ruby-sdk/issues/29)) 9 | 10 | ### Bug Fixes 11 | 12 | * Refactor resources params to reflect better API payload structure ([#29](https://github.com/Open-Pix/ruby-sdk/issues/29)) ([c03692a](https://github.com/Open-Pix/ruby-sdk/commit/c03692acc02e83d66f3884a1d49d03941bc8a221)) 13 | 14 | ## [1.2.0](https://github.com/Open-Pix/ruby-sdk/compare/v1.1.1...v1.2.0) (2023-07-04) 15 | 16 | 17 | ### Features 18 | 19 | * add ability to accept custom api url and version ([#26](https://github.com/Open-Pix/ruby-sdk/issues/26)) ([cae73ef](https://github.com/Open-Pix/ruby-sdk/commit/cae73ef01bd474e27bcd5246ff3b374f9bfd76e1)) 20 | 21 | ## [1.1.1](https://github.com/Open-Pix/ruby-sdk/compare/v1.1.0...v1.1.1) (2023-07-04) 22 | 23 | 24 | ### Bug Fixes 25 | 26 | * Fix not authorized response parsing ([#24](https://github.com/Open-Pix/ruby-sdk/issues/24)) ([00f508d](https://github.com/Open-Pix/ruby-sdk/commit/00f508d4463b25b774ca4c0449c3cd050fdad549)) 27 | 28 | ## [1.1.0](https://github.com/Open-Pix/ruby-sdk/compare/v1.0.0...v1.1.0) (2023-06-25) 29 | 30 | 31 | ### Features 32 | 33 | * add utility method for webhook payload verify ([#19](https://github.com/Open-Pix/ruby-sdk/issues/19)) ([1aaf042](https://github.com/Open-Pix/ruby-sdk/commit/1aaf042144951df2a0f9f84c79942c17670f7bef)) 34 | 35 | ## 1.0.0 (2023-06-13) 36 | 37 | 38 | ### Features 39 | 40 | * Trigger CI ([54531e8](https://github.com/Open-Pix/ruby-sdk/commit/54531e83fb3ebf520df077897de0f99488ded6db)) 41 | 42 | ## [0.1.0] - 2023-05-09 43 | 44 | - Initial release 45 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | Using welcoming and inclusive language 12 | Being respectful of differing viewpoints and experiences 13 | Gracefully accepting constructive criticism 14 | Focusing on what is best for the community 15 | Showing empathy towards other community members 16 | Examples of unacceptable behavior by participants include: 17 | 18 | The use of sexualized language or imagery and unwelcome sexual attention or advances 19 | Trolling, insulting/derogatory comments, and personal or political attacks 20 | Public or private harassment 21 | Publishing others' private information, such as a physical or electronic address, without explicit permission 22 | Other conduct which could reasonably be considered inappropriate in a professional setting 23 | Our Responsibilities 24 | As project maintainers, we are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 25 | 26 | As contributors, you have the responsibility to adhere to these standards and report any instances of unacceptable behavior. 27 | 28 | ## Enforcement 29 | 30 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the project team at . The project team will review and investigate all complaints and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 31 | 32 | Project maintainers who do not follow or enforce the code of conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 33 | 34 | ## Attribution 35 | 36 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4, available at http://contributor-covenant.org/version/1/4. 37 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | [code-of-conduct]: CODE_OF_CONDUCT.md 4 | 5 | We welcome contributions to the Woovi Ruby SDK! If you would like to contribute, please follow these steps: 6 | 7 | - Fork the repository 8 | - Create a new branch for your feature or bug fix 9 | - Write your code and tests 10 | - Commit your changes and push your branch to GitHub 11 | - Submit a pull request 12 | 13 | Please make sure to adhere to the [code of conduct][code-of-conduct]. -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | 5 | # Specify your gem's dependencies in openpix-ruby_sdk.gemspec 6 | gemspec 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 OpenPix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # OpenPix/Woovi Ruby SDK 2 | 3 | Welcome to the Woovi Ruby SDK! This SDK provides convenient access to the Woovi REST API, allowing you to easily integrate Woovi's REST API into your Ruby applications. 4 | 5 | ## Documentation 6 | 7 | Documentation for Woovi REST API can be found [here](https://developers.woovi.com/api). 8 | RDoc documentation for classes included in the gem can be found [here](). 9 | 10 | ## Installation 11 | 12 | To install this gem using Bundler, add this following line to your `Gemfile`. 13 | 14 | ```shell 15 | gem 'openpix-ruby_sdk', '~> 0.1.0' 16 | ``` 17 | 18 | To manually install `openpix-ruby_sdk` via Rubygems simply: 19 | 20 | ```shell 21 | gem install openpix-ruby_sdk -v 1.0.0 22 | ``` 23 | 24 | ## Usage 25 | 26 | Main class `openpix/ruby_sdk/client` is your entrypoint to the endpoints. 27 | 28 | ### Authenticating client 29 | 30 | ```ruby 31 | require 'openpix/ruby_sdk' 32 | 33 | # Your AppID from https://app.openpix.com/home/applications/tab/list 34 | app_id = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx' 35 | 36 | client = Openpix::RubySdk::Client.new(app_id) 37 | ``` 38 | 39 | ### Using resources 40 | 41 | `Openpix::RubySdk::Client` has access to all resources available through a accessor method with resource name in plural form 42 | E.g: Charge -> client.charges (returns the charge resource class with all available methods) 43 | 44 | ```ruby 45 | # Creating a Charge 46 | client.charges.init_body( 47 | params: { 48 | correlation_id: 'my-correlation-id', 49 | value: 50000 50 | } 51 | ) 52 | response = client.charges.save 53 | response.success? # should yield true 54 | response.resource_response # API response for this resource, example bellow \/ 55 | # { 56 | # "status" => "ACTIVE", 57 | # "value" => 100, 58 | # "comment" => "good", 59 | # "correlationID" => "9134e286-6f71-427a-bf00-241681624586", 60 | # ... and so on 61 | # } 62 | 63 | # Listing Charges 64 | # Default skip is 0 and limit is 100 65 | response = client.charges.fetch(skip: 0, limit: 100) # skip and limit are pagination params, https://developers.woovi.com/api#tag/charge/paths/~1api~1v1~1charge/get 66 | response.pagination_meta # holds information about pagination, like total, hasNextPage and so on 67 | response.resource_response # API response for this resource, should be an array 68 | 69 | # If next or previous pages available, there is a convenience method to fetch next or previous pages 70 | # In order to call those methods, you need first to call #fetch or #fetch! to set the pagination params 71 | # Those methods will preserve any :params sent to #fetch or #fetch! method 72 | # BE CAREFUL, those methods only have bang! versions because they have a strong dependency on #fetch, handle properly their errors 73 | client.charges.fetch_next_page! 74 | client.charges.fetch_previous_page! 75 | 76 | # Finding Charge 77 | response = client.charges.find(id: 'my-charge-id') 78 | # response has same attributes from save, since it is a single resource response 79 | 80 | # Destroying Charge 81 | response = client.charges.destroy(id: 'my-charge-id') 82 | response.success? # this operations just returns success 83 | ``` 84 | 85 | ### Available resources 86 | 87 | The available resources are: 88 | 89 | - Charge (charges) 90 | - Customer (customers) 91 | - Payment (payments) 92 | - Refund (refunds) 93 | - Subscription (subscriptions) 94 | - Webhook (webhooks) 95 | 96 | ### Handling errors 97 | 98 | All available resource methods have their bang! version, which raises an error whenever something goes wrong so you can properly handle those cases 99 | All errors have some helpful message, showing response status and error response from API 100 | 101 | Error classes are: 102 | **save!** -> `Openpix::RubySdk::Resources::RequestError` 103 | **fetch!** -> `Openpix::RubySdk::Resources::RequestError` 104 | **fetch_next_page!** -> `Openpix::RubySdk::Resources::RequestError`, `Openpix::RubySdk::Resources::NotFetchedError`, `Openpix::RubySdk::Resources::PageNotDefinedError` 105 | **fetch_previous_page!** -> `Openpix::RubySdk::Resources::RequestError`, `Openpix::RubySdk::Resources::NotFetchedError`, `Openpix::RubySdk::Resources::PageNotDefinedError` 106 | **find!** -> `Openpix::RubySdk::Resources::RequestError` 107 | **destroy!** -> `Openpix::RubySdk::Resources::RequestError` 108 | 109 | For the safe version (without bang!) there will be an `error_response` attribute setted in the API response whenever `success?` is false. 110 | 111 | ```ruby 112 | response = client.customers.save 113 | 114 | unless response.success? 115 | response.error_response # error response from API 116 | end 117 | ``` 118 | 119 | ## Contributing 120 | 121 | If you have suggestions for how Woovi Ruby SDK could be improved, or want to report a bug, open an issue! We'd love all and any contributions. 122 | 123 | For more, check out the [Contributing Guide](CONTRIBUTING.md). 124 | 125 | ## License 126 | 127 | Woovi Ruby SDK is distributed under the terms of the [MIT license](LICENSE). 128 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'bundler/gem_tasks' 4 | require 'rspec/core/rake_task' 5 | 6 | RSpec::Core::RakeTask.new(:spec) 7 | 8 | require 'rubocop/rake_task' 9 | 10 | RuboCop::RakeTask.new 11 | 12 | task default: %i[spec rubocop] 13 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # frozen_string_literal: true 3 | 4 | require 'bundler/setup' 5 | require 'openpix/ruby_sdk' 6 | 7 | # You can add fixtures and/or initialization code here to make experimenting 8 | # with your gem easier. You can also use a different console, if you like. 9 | 10 | require 'irb' 11 | IRB.start(__FILE__) 12 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/version' 4 | require 'openpix/ruby_sdk/client' 5 | 6 | module Openpix 7 | # Main module 8 | module RubySdk 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/api_body_formatter.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'active_support' 4 | require 'active_support/core_ext/string/inflections' 5 | 6 | module Openpix 7 | module RubySdk 8 | # Helper class to format body params before request 9 | class ApiBodyFormatter 10 | class << self 11 | def remove_empty_values(entity) 12 | entity.compact.reject do |_key, value| 13 | value.empty? if value.respond_to?(:empty?) 14 | end 15 | end 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/api_response.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Openpix 4 | module RubySdk 5 | # An Object representing the response from a call to Woovi API 6 | class ApiResponse 7 | attr_reader :success, :body, :status 8 | 9 | def initialize(status:, body:, single_resource: nil, collection_resource: nil) 10 | @success = status == 200 11 | @status = status 12 | @body = body 13 | @single_resource = single_resource 14 | @collection_resource = collection_resource 15 | end 16 | 17 | def success? 18 | success 19 | end 20 | 21 | def resource_response 22 | return @body[@single_resource] if @single_resource 23 | 24 | @body[@collection_resource] 25 | end 26 | 27 | def error_response 28 | return @body['error'] if @body['error'] 29 | return @body['errors'].first['message'] if @body['errors'] && !@body['errors'].empty? 30 | 31 | '' 32 | end 33 | 34 | def pagination_meta 35 | return @body['pageInfo'] if @body['pageInfo'] 36 | 37 | {} 38 | end 39 | end 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'active_support' 4 | require 'active_support/core_ext/string/inflections' 5 | 6 | require 'openpix/ruby_sdk/http_client' 7 | require 'openpix/ruby_sdk/resources' 8 | 9 | module Openpix 10 | module RubySdk 11 | # Entrypoint class, expect to access resources and other classes just from this class 12 | class Client 13 | RESOURCES = %w[ 14 | Charge 15 | Customer 16 | Payment 17 | Refund 18 | Subscription 19 | Webhook 20 | ].freeze 21 | 22 | def initialize(auth_token) 23 | @auth_token = auth_token 24 | 25 | init_http_client 26 | end 27 | 28 | RESOURCES.each do |resource| 29 | pluralized_resource_name = resource.downcase.pluralize 30 | define_method pluralized_resource_name do 31 | return instance_variable_get("@#{pluralized_resource_name}") if instance_variable_get("@#{pluralized_resource_name}") 32 | 33 | instance_variable_set( 34 | "@#{pluralized_resource_name}", 35 | "Openpix::RubySdk::Resources::#{resource}".constantize.new(@http_client) 36 | ) 37 | end 38 | end 39 | 40 | private 41 | 42 | def init_http_client 43 | @http_client = Openpix::RubySdk::HttpClient.instance 44 | 45 | @http_client.initialize_http_client(@auth_token) 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/http_client.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'faraday' 4 | require 'faraday/httpclient' 5 | 6 | module Openpix 7 | module RubySdk 8 | # Application HTTP Client to make requests, it uses Faraday as the wrapper and defaults to STD Lib client (Net::HTTP) 9 | class HttpClient 10 | BASE_URL = 'https://api.woovi.com/api' 11 | API_VERSION = '/v1' 12 | 13 | @instance = new 14 | 15 | private_class_method :new 16 | 17 | class << self 18 | attr_reader :instance 19 | end 20 | 21 | def initialize_http_client(auth_token, api_url = BASE_URL, api_version = API_VERSION) 22 | @http_client = Faraday.new( 23 | url: "#{api_url}#{api_version}", 24 | headers: { 25 | 'Authorization' => auth_token 26 | } 27 | ) do |f| 28 | f.request :json 29 | f.response :json 30 | f.adapter :httpclient 31 | end 32 | end 33 | 34 | def post(resource, body:, headers: {}, params: {}) 35 | @http_client.post(resource) do |request| 36 | request.params = params 37 | request.headers = request.headers.merge(headers) 38 | request.body = body 39 | end 40 | end 41 | 42 | def get(resource, params: {}, headers: {}) 43 | @http_client.get(resource) do |request| 44 | request.params = params 45 | request.headers = request.headers.merge(headers) 46 | end 47 | end 48 | 49 | def delete(resource, headers: {}) 50 | @http_client.delete(resource) do |request| 51 | request.headers = request.headers.merge(headers) 52 | end 53 | end 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/resources/charge' 4 | require 'openpix/ruby_sdk/resources/customer' 5 | require 'openpix/ruby_sdk/resources/payment' 6 | require 'openpix/ruby_sdk/resources/refund' 7 | require 'openpix/ruby_sdk/resources/subscription' 8 | require 'openpix/ruby_sdk/resources/webhook' 9 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources/charge.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/resources/resource' 4 | require 'openpix/ruby_sdk/api_body_formatter' 5 | 6 | module Openpix 7 | module RubySdk 8 | module Resources 9 | # Make API operations on Charge resource 10 | class Charge < Resource 11 | ATTRS = %w[ 12 | correlationID 13 | value 14 | type 15 | comment 16 | identifier 17 | expiresIn 18 | customer 19 | daysForDueDate 20 | daysAfterDueDate 21 | interests 22 | fines 23 | additionalInfo 24 | ].freeze 25 | 26 | attr_accessor(*ATTRS) 27 | 28 | # @param params [Hash] the attributes for creating a Charge 29 | # @param rest [Hash] more attributes to be merged at the body, use this only for unsupported fields 30 | def init_body(params: {}, rest: {}) 31 | super(base_attrs: ATTRS, params: params, rest: rest) 32 | end 33 | 34 | # attributes used on POST create method 35 | def create_attributes 36 | ATTRS 37 | end 38 | 39 | # URL for this resource 40 | def to_url 41 | 'charge' 42 | end 43 | 44 | # Converts its attributes into a hash 45 | def to_body 46 | body = super 47 | 48 | return body if body['customer'].nil? || body['customer'].empty? 49 | 50 | body['customer'] = Openpix::RubySdk::ApiBodyFormatter.remove_empty_values(body['customer']) 51 | 52 | return body if body['customer']['address'].nil? || body['customer']['address'].empty? 53 | 54 | customer_address_parsed = Openpix::RubySdk::ApiBodyFormatter.remove_empty_values(body['customer']['address']) 55 | body['customer'] = body['customer'].merge({ 'address' => customer_address_parsed }) 56 | 57 | body 58 | end 59 | 60 | # add a new additional_info 61 | # @param key [String] the key 62 | # @param value [String] the value 63 | def add_additional_info(key, value) 64 | @additionalInfo = [] if @additionalInfo.nil? 65 | 66 | @additionalInfo << { 'key' => key, 'value' => value } 67 | end 68 | 69 | # set interests configuration for creating this resource 70 | # @param value [Number] value in basis points of interests to be applied daily after the charge hits the deadline 71 | def set_interests(value) 72 | @interests = { 'value' => value } 73 | end 74 | 75 | # set fines configuration for creating this resource 76 | # @param value [Number] value in basis points of fines to be applied when the charge hits the deadline 77 | def set_fines(value) 78 | @fines = { 'value' => value } 79 | end 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources/customer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/resources/resource' 4 | require 'openpix/ruby_sdk/api_body_formatter' 5 | 6 | module Openpix 7 | module RubySdk 8 | module Resources 9 | # Make API operations on Customer resource 10 | class Customer < Resource 11 | ATTRS = %w[ 12 | name 13 | email 14 | phone 15 | taxID 16 | correlationID 17 | address 18 | ].freeze 19 | 20 | attr_accessor(*ATTRS) 21 | 22 | # @param params [Hash{String => String, Number, Hash{String, Number}, Array}] the attributes for creating a Charge 23 | # @param rest [Hash] more attributes to be merged at the body, use this only for unsupported fields 24 | def init_body(params: {}, rest: {}) 25 | super(base_attrs: ATTRS, params: params, rest: rest) 26 | end 27 | 28 | def create_attributes 29 | ATTRS 30 | end 31 | 32 | def to_url 33 | 'customer' 34 | end 35 | 36 | def to_body 37 | body = super 38 | 39 | return body if body['address'].nil? || body['address'].empty? 40 | 41 | body['address'] = Openpix::RubySdk::ApiBodyFormatter.remove_empty_values(body['address']) 42 | 43 | body 44 | end 45 | 46 | # rubocop:disable Lint/UnusedMethodArgument 47 | def destroy(id:) 48 | raise( 49 | ActionNotImplementedError, 50 | 'customer does not implement DELETE action' 51 | ) 52 | end 53 | 54 | def destroy!(id:) 55 | raise( 56 | ActionNotImplementedError, 57 | 'customer does not implement DELETE action' 58 | ) 59 | end 60 | # rubocop:enable Lint/UnusedMethodArgument 61 | end 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources/payment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'active_support' 4 | require 'active_support/core_ext/hash/except' 5 | 6 | require 'openpix/ruby_sdk/resources/resource' 7 | 8 | module Openpix 9 | module RubySdk 10 | module Resources 11 | # Make API operations on Payment resource 12 | class Payment < Resource 13 | ATTRS = %w[ 14 | value 15 | destinationAlias 16 | correlationID 17 | comment 18 | sourceAccountId 19 | ].freeze 20 | 21 | attr_accessor(*ATTRS) 22 | 23 | # @param params [Hash{String => String, Number, Hash{String, Number}, Array}] the attributes for creating a Charge 24 | # @param rest [Hash] more attributes to be merged at the body, use this only for unsupported fields 25 | def init_body(params: {}, rest: {}) 26 | super(base_attrs: ATTRS, params: params, rest: rest) 27 | end 28 | 29 | def create_attributes 30 | ATTRS 31 | end 32 | 33 | def to_url 34 | 'payment' 35 | end 36 | 37 | # rubocop:disable Lint/UnusedMethodArgument 38 | def destroy(id:) 39 | raise( 40 | ActionNotImplementedError, 41 | 'payment does not implement DELETE action' 42 | ) 43 | end 44 | 45 | def destroy!(id:) 46 | raise( 47 | ActionNotImplementedError, 48 | 'payment does not implement DELETE action' 49 | ) 50 | end 51 | # rubocop:enable Lint/UnusedMethodArgument 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources/refund.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'active_support' 4 | require 'active_support/core_ext/hash/except' 5 | 6 | require 'openpix/ruby_sdk/resources/resource' 7 | 8 | module Openpix 9 | module RubySdk 10 | module Resources 11 | # Make API operations on Refund resource 12 | class Refund < Resource 13 | ATTRS = %w[ 14 | value 15 | transactionEndToEndId 16 | correlationID 17 | comment 18 | ].freeze 19 | 20 | attr_accessor(*ATTRS) 21 | 22 | # @param params [Hash{String => String, Number, Hash{String, Number}, Array}] the attributes for creating a Charge 23 | # @param rest [Hash] more attributes to be merged at the body, use this only for unsupported fields 24 | def init_body(params: {}, rest: {}) 25 | super(base_attrs: ATTRS, params: params, rest: rest) 26 | end 27 | 28 | def create_attributes 29 | ATTRS 30 | end 31 | 32 | def to_url 33 | 'refund' 34 | end 35 | 36 | # rubocop:disable Lint/UnusedMethodArgument 37 | def destroy(id:) 38 | raise( 39 | ActionNotImplementedError, 40 | 'refund does not implement DELETE action' 41 | ) 42 | end 43 | 44 | def destroy!(id:) 45 | raise( 46 | ActionNotImplementedError, 47 | 'refund does not implement DELETE action' 48 | ) 49 | end 50 | # rubocop:enable Lint/UnusedMethodArgument 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources/resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'active_support' 4 | require 'active_support/core_ext/string/inflections' 5 | require 'active_support/core_ext/hash/indifferent_access' 6 | 7 | require 'openpix/ruby_sdk/api_response' 8 | require 'openpix/ruby_sdk/api_body_formatter' 9 | 10 | module Openpix 11 | module RubySdk 12 | module Resources 13 | # Error returned when the required method is not implemented in the class child class 14 | class NotImplementedError < StandardError 15 | def initialize(msg: nil, method: 'nil') 16 | super(msg) if msg.present? 17 | 18 | super("#{method} not implemented") 19 | end 20 | end 21 | 22 | # Error raised when there is a status != from 200 from the API response 23 | # This is just raised in methods that calls the bang (with exclamation mark "!") version 24 | class RequestError < StandardError 25 | end 26 | 27 | # Error raised when client is trying to fetch next/previous page without fetching without pagination first 28 | class NotFetchedError < StandardError 29 | end 30 | 31 | # Error raised when client is trying to fetch next/previous page and page does not exists 32 | class PageNotDefinedError < StandardError 33 | end 34 | 35 | # Error raised when client is trying to use a Restful action that is not implemented in the current resource 36 | class ActionNotImplementedError < StandardError 37 | end 38 | 39 | # Base class for resources from the API 40 | class Resource 41 | def initialize(http_client) 42 | @http_client = http_client 43 | end 44 | 45 | def init_body(base_attrs: [], params: {}, rest: {}) 46 | params = params.with_indifferent_access 47 | base_attrs.each { |attr| instance_variable_set("@#{attr}", params[attr]) } 48 | @rest = rest 49 | 50 | self 51 | end 52 | 53 | def to_url 54 | raise NotImplementedError.new(method: __method__) 55 | end 56 | 57 | def to_single_resource 58 | to_url 59 | end 60 | 61 | def to_collection_resource 62 | to_url.pluralize 63 | end 64 | 65 | def create_attributes 66 | raise NotImplementedError.new(method: __method__) 67 | end 68 | 69 | def to_body 70 | body = {} 71 | 72 | create_attributes.each do |attr| 73 | body[attr] = send(attr) 74 | end 75 | 76 | compacted_body = Openpix::RubySdk::ApiBodyFormatter.remove_empty_values(body) 77 | 78 | return compacted_body unless @rest 79 | 80 | compacted_body.merge(@rest) 81 | end 82 | 83 | def save(extra_headers: {}, return_existing: false) 84 | response = post_request(extra_headers, return_existing) 85 | 86 | Openpix::RubySdk::ApiResponse.new( 87 | status: response.status, 88 | body: response.body, 89 | single_resource: to_single_resource 90 | ) 91 | end 92 | 93 | def save!(extra_headers: {}, return_existing: false) 94 | response = post_request(extra_headers, return_existing) 95 | api_response = Openpix::RubySdk::ApiResponse.new( 96 | status: response.status, 97 | body: response.body, 98 | single_resource: to_single_resource 99 | ) 100 | 101 | if response.status != 200 102 | raise( 103 | RequestError, 104 | "Error while saving, API response: #{api_response.error_response}, status: #{api_response.status}" 105 | ) 106 | end 107 | 108 | api_response 109 | end 110 | 111 | def fetch(skip: nil, limit: nil, extra_headers: {}, params: {}) 112 | set_pagination(skip, limit) 113 | 114 | response = get_request(extra_headers: extra_headers, params: @pagination_params.merge(params)) 115 | api_response = Openpix::RubySdk::ApiResponse.new( 116 | status: response.status, 117 | body: response.body, 118 | collection_resource: to_collection_resource 119 | ) 120 | 121 | @fetched = api_response.status == 200 122 | 123 | set_pagination_meta(api_response.pagination_meta) if @fetched 124 | @last_fetched_params = params if @fetched && !params.empty? 125 | 126 | api_response 127 | end 128 | 129 | def fetch!(skip: nil, limit: nil, extra_headers: {}, params: {}) 130 | set_pagination(skip, limit) 131 | 132 | response = get_request(extra_headers: extra_headers, params: @pagination_params.merge(params)) 133 | api_response = Openpix::RubySdk::ApiResponse.new( 134 | status: response.status, 135 | body: response.body, 136 | collection_resource: to_collection_resource 137 | ) 138 | 139 | if response.status != 200 140 | raise( 141 | RequestError, 142 | "Error while fetching, API response: #{api_response.error_response}, status: #{api_response.status}" 143 | ) 144 | end 145 | 146 | @fetched = true 147 | @last_fetched_params = params unless params.empty? 148 | set_pagination_meta(api_response.pagination_meta) 149 | 150 | api_response 151 | end 152 | 153 | def fetch_next_page!(extra_headers: {}) 154 | fetch_page!(:next, extra_headers) 155 | end 156 | 157 | def fetch_previous_page!(extra_headers: {}) 158 | fetch_page!(:previous, extra_headers) 159 | end 160 | 161 | def find(id:, extra_headers: {}) 162 | response = get_request(url: encoded_url(id), extra_headers: extra_headers) 163 | 164 | Openpix::RubySdk::ApiResponse.new( 165 | status: response.status, 166 | body: response.body, 167 | single_resource: to_single_resource 168 | ) 169 | end 170 | 171 | def find!(id:, extra_headers: {}) 172 | response = get_request(url: encoded_url(id), extra_headers: extra_headers) 173 | api_response = Openpix::RubySdk::ApiResponse.new( 174 | status: response.status, 175 | body: response.body, 176 | single_resource: to_single_resource 177 | ) 178 | 179 | if response.status != 200 180 | raise( 181 | RequestError, 182 | "Error while getting #{to_single_resource} of id = #{id}, API response: #{api_response.error_response}, status: #{api_response.status}" 183 | ) 184 | end 185 | 186 | api_response 187 | end 188 | 189 | def destroy(id:, extra_headers: {}) 190 | response = delete_request(url: encoded_url(id), extra_headers: extra_headers) 191 | 192 | Openpix::RubySdk::ApiResponse.new( 193 | status: response.status, 194 | body: response.body, 195 | single_resource: to_single_resource 196 | ) 197 | end 198 | 199 | def destroy!(id:, extra_headers: {}) 200 | response = delete_request(url: encoded_url(id), extra_headers: extra_headers) 201 | api_response = Openpix::RubySdk::ApiResponse.new( 202 | status: response.status, 203 | body: response.body, 204 | single_resource: to_single_resource 205 | ) 206 | 207 | if response.status != 200 208 | raise( 209 | RequestError, 210 | "Error while deleting #{to_url} of id = #{id}, API response: #{api_response.error_response}, status: #{api_response.status}" 211 | ) 212 | end 213 | 214 | api_response 215 | end 216 | 217 | private 218 | 219 | def post_request(extra_headers, return_existing) 220 | @http_client.post( 221 | to_url, 222 | body: to_body, 223 | headers: extra_headers, 224 | params: { return_existing: return_existing } 225 | ) 226 | end 227 | 228 | def get_request(url: to_url, extra_headers: {}, params: {}) 229 | @http_client.get( 230 | url, 231 | headers: extra_headers, 232 | params: params 233 | ) 234 | end 235 | 236 | def delete_request(url: to_url, extra_headers: {}) 237 | @http_client.delete( 238 | url, 239 | headers: extra_headers 240 | ) 241 | end 242 | 243 | def set_pagination(skip, limit) 244 | @pagination_params = { skip: 0, limit: 100 } if @pagination_params.nil? 245 | @pagination_params[:skip] = 0 if @pagination_params[:skip].nil? 246 | @pagination_params[:limit] = 100 if @pagination_params[:limit].nil? 247 | 248 | @pagination_params[:skip] = skip if skip 249 | 250 | return unless limit 251 | 252 | @pagination_params[:limit] = limit 253 | end 254 | 255 | def set_pagination_meta(pagination_meta) 256 | @pagination_meta = { 257 | total_count: pagination_meta['totalCount'], 258 | has_previous_page: pagination_meta['hasPreviousPage'], 259 | has_next_page: pagination_meta['hasNextPage'] 260 | } 261 | end 262 | 263 | def calculate_pagination_params(page_orientation) 264 | if page_orientation == :next 265 | @pagination_params[:skip] += @pagination_params[:limit] 266 | elsif page_orientation == :previous 267 | @pagination_params[:skip] -= @pagination_params[:limit] 268 | end 269 | end 270 | 271 | def fetch_page!(page_orientation, extra_headers = {}) 272 | unless @fetched 273 | raise( 274 | NotFetchedError, 275 | "#fetch needs to be called before trying to fetch #{page_orientation} page" 276 | ) 277 | end 278 | 279 | unless @pagination_meta[:"has_#{page_orientation}_page"] 280 | raise( 281 | PageNotDefinedError, 282 | "There is no #{page_orientation} page defined for the skip: #{@pagination_params[:skip]} and " \ 283 | "limit: #{@pagination_params[:limit]} params requested" 284 | ) 285 | end 286 | 287 | calculate_pagination_params(page_orientation) 288 | 289 | request_params = if @last_fetched_params.nil? 290 | @pagination_params 291 | else 292 | @pagination_params.merge(@last_fetched_params) 293 | end 294 | response = get_request(extra_headers: extra_headers, params: request_params) 295 | api_response = Openpix::RubySdk::ApiResponse.new( 296 | status: response.status, 297 | body: response.body, 298 | collection_resource: to_collection_resource 299 | ) 300 | 301 | if response.status != 200 302 | raise( 303 | RequestError, 304 | "Error while fetching #{page_orientation} page, API response: #{api_response.error_response}, status: #{api_response.status}" 305 | ) 306 | end 307 | 308 | set_pagination_meta(api_response.pagination_meta) 309 | 310 | api_response 311 | end 312 | 313 | def encoded_url(id) 314 | "#{to_url}/#{URI.encode_www_form_component(id)}" 315 | end 316 | end 317 | end 318 | end 319 | end 320 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources/subscription.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/resources/resource' 4 | require 'openpix/ruby_sdk/api_body_formatter' 5 | 6 | module Openpix 7 | module RubySdk 8 | module Resources 9 | # Make API operations on Subscription resource 10 | class Subscription < Resource 11 | ATTRS = %w[ 12 | value 13 | dayGenerateCharge 14 | customer 15 | chargeType 16 | ].freeze 17 | 18 | attr_accessor(*ATTRS) 19 | 20 | # @param params [Hash{String => String, Number, Hash{String, Number}, Array}] the attributes for creating a Charge 21 | # @param rest [Hash] more attributes to be merged at the body, use this only for unsupported fields 22 | def init_body(params: {}, rest: {}) 23 | super(base_attrs: ATTRS, params: params, rest: rest) 24 | end 25 | 26 | def create_attributes 27 | ATTRS 28 | end 29 | 30 | def to_url 31 | 'subscriptions' 32 | end 33 | 34 | def to_single_resource 35 | 'subscription' 36 | end 37 | 38 | def to_body 39 | body = super 40 | 41 | return body if body['customer'].nil? || body['customer'].empty? 42 | 43 | body['customer'] = Openpix::RubySdk::ApiBodyFormatter.remove_empty_values(body['customer']) 44 | 45 | body 46 | end 47 | 48 | # rubocop:disable Lint/UnusedMethodArgument 49 | def fetch(skip: nil, limit: nil, extra_headers: {}) 50 | raise( 51 | ActionNotImplementedError, 52 | 'subscription does not implement GET index action' 53 | ) 54 | end 55 | 56 | def fetch!(skip: nil, limit: nil, extra_headers: {}) 57 | raise( 58 | ActionNotImplementedError, 59 | 'subscription does not implement GET index action' 60 | ) 61 | end 62 | 63 | def fetch_next_page!(extra_headers: {}) 64 | raise( 65 | ActionNotImplementedError, 66 | 'subscription does not implement GET index action' 67 | ) 68 | end 69 | 70 | def fetch_previous_page!(extra_headers: {}) 71 | raise( 72 | ActionNotImplementedError, 73 | 'subscription does not implement GET index action' 74 | ) 75 | end 76 | 77 | def destroy(id:) 78 | raise( 79 | ActionNotImplementedError, 80 | 'subscription does not implement DELETE action' 81 | ) 82 | end 83 | 84 | def destroy!(id:) 85 | raise( 86 | ActionNotImplementedError, 87 | 'subscription does not implement DELETE action' 88 | ) 89 | end 90 | # rubocop:enable Lint/UnusedMethodArgument 91 | end 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/resources/webhook.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/resources/resource' 4 | 5 | module Openpix 6 | module RubySdk 7 | module Resources 8 | # Make API operations on Webhook resource 9 | class Webhook < Resource 10 | ATTRS = %w[ 11 | name 12 | event 13 | url 14 | authorization 15 | isActive 16 | ].freeze 17 | 18 | attr_accessor(*ATTRS) 19 | 20 | # @param params [Hash{String => String, Number, Hash{String, Number}, Array}] the attributes for creating a Charge 21 | # @param rest [Hash] more attributes to be merged at the body, use this only for unsupported fields 22 | def init_body(params: {}, rest: {}) 23 | super(base_attrs: ATTRS, params: params, rest: rest) 24 | end 25 | 26 | def create_attributes 27 | ATTRS 28 | end 29 | 30 | def to_url 31 | 'webhook' 32 | end 33 | 34 | def to_body 35 | body = super 36 | 37 | { webhook: body } 38 | end 39 | 40 | # rubocop:disable Lint/UnusedMethodArgument 41 | def find(id:, extra_headers: {}) 42 | raise( 43 | ActionNotImplementedError, 44 | 'webhook does not implement GET show action' 45 | ) 46 | end 47 | 48 | def find!(id:, extra_headers: {}) 49 | raise( 50 | ActionNotImplementedError, 51 | 'webhook does not implement GET show action' 52 | ) 53 | end 54 | # rubocop:enable Lint/UnusedMethodArgument 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/utils.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'base64' 4 | require 'openssl' 5 | 6 | module Openpix 7 | module RubySdk 8 | # Entrypoint class to access utility methods to facilitate interaction with our APIs 9 | class Utils 10 | BASE64_PUB_KEY = 'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlHZk1BMEdDU3FHU0liM0RRRUJBUVVBQTRHTkFEQ0JpUUtCZ1FDLytOdElranpldnZxRCtJM01NdjNiTFhEdApwdnhCalk0QnNSclNkY2EzcnRBd01jUllZdnhTbmQ3amFnVkxwY3RNaU94UU84aWVVQ0tMU1dIcHNNQWpPL3paCldNS2Jxb0c4TU5waS91M2ZwNnp6MG1jSENPU3FZc1BVVUcxOWJ1VzhiaXM1WloySVpnQk9iV1NwVHZKMGNuajYKSEtCQUE4MkpsbitsR3dTMU13SURBUUFCCi0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=' 11 | PUB_KEY_INSTANCE = OpenSSL::PKey::RSA.new(Base64.decode64(BASE64_PUB_KEY)) 12 | 13 | class << self 14 | def verify_signature(base64_signature, payload) 15 | PUB_KEY_INSTANCE.verify( 16 | OpenSSL::Digest.new('SHA256'), 17 | Base64.decode64(base64_signature), 18 | payload 19 | ) 20 | end 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /lib/openpix/ruby_sdk/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Openpix 4 | module RubySdk 5 | VERSION = '2.0.0' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /openpix-ruby_sdk.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'lib/openpix/ruby_sdk/version' 4 | 5 | Gem::Specification.new do |spec| 6 | spec.name = 'openpix-ruby_sdk' 7 | spec.version = Openpix::RubySdk::VERSION 8 | spec.authors = ['Erick Takeshi'] 9 | spec.email = ['erick.tmr@outlook.com'] 10 | 11 | spec.summary = 'Ruby SDK for OpenPix/Woovi API' 12 | spec.description = 'Ruby SDK for OpenPix/Woovi API' 13 | spec.homepage = 'https://github.com/Open-Pix/ruby-sdk' 14 | spec.required_ruby_version = '>= 2.6.0' 15 | 16 | spec.metadata['homepage_uri'] = spec.homepage 17 | spec.metadata['source_code_uri'] = 'https://github.com/Open-Pix/ruby-sdk' 18 | spec.metadata['changelog_uri'] = 'https://github.com/Open-Pix/ruby-sdk/blob/main/CHANGELOG.md' 19 | 20 | # Specify which files should be added to the gem when it is released. 21 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 22 | spec.files = Dir['lib/**/*.rb'] 23 | spec.require_paths = ['lib'] 24 | spec.extra_rdoc_files = Dir['README.md', 'CHANGELOG.md'] 25 | 26 | # Dependencies 27 | spec.add_dependency 'activesupport', '>= 6.1' 28 | spec.add_dependency 'faraday', '~> 2.7', '>= 2.7.4' 29 | spec.add_dependency 'faraday-httpclient', '~> 2.0', '>= 2.0.1' 30 | spec.add_dependency 'mutex_m' 31 | 32 | spec.add_development_dependency 'pry' 33 | spec.add_development_dependency 'rack', '~> 3.0' 34 | spec.add_development_dependency 'rake' 35 | spec.add_development_dependency 'rspec', '~> 3.12' 36 | spec.add_development_dependency 'rubocop', '~> 1.50' 37 | spec.add_development_dependency 'webrick', '~> 1.8' 38 | spec.add_development_dependency 'yard', '~> 0.9.34' 39 | end 40 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/api_response_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/api_response' 4 | 5 | RSpec.describe Openpix::RubySdk::ApiResponse do 6 | let(:status) { nil } 7 | let(:body) { {} } 8 | let(:single_resource) { nil } 9 | let(:collection_resource) { nil } 10 | 11 | subject do 12 | described_class.new( 13 | status: status, 14 | body: body, 15 | single_resource: single_resource, 16 | collection_resource: collection_resource 17 | ) 18 | end 19 | 20 | context 'with status 200' do 21 | let(:status) { 200 } 22 | 23 | describe '#success' do 24 | it 'returns true' do 25 | expect(subject.success).to eq(true) 26 | end 27 | end 28 | end 29 | 30 | context 'with status != 200' do 31 | let(:status) { 400 } 32 | 33 | describe '#success' do 34 | it 'returns false' do 35 | expect(subject.success).to eq(false) 36 | end 37 | end 38 | end 39 | 40 | describe '#resource_response' do 41 | context 'with single resource' do 42 | let(:single_resource) { 'charge' } 43 | let(:resource) { { 'key' => 'value' } } 44 | let(:body) { { 'charge' => resource } } 45 | 46 | it 'returns resource' do 47 | expect(subject.resource_response).to eq(resource) 48 | end 49 | end 50 | 51 | context 'with collection resource' do 52 | let(:collection_resource) { 'charges' } 53 | let(:resource) { [{ 'key' => 'value' }] } 54 | let(:body) { { 'charges' => resource } } 55 | 56 | it 'returns resources collection' do 57 | expect(subject.resource_response).to eq(resource) 58 | end 59 | end 60 | end 61 | 62 | describe '#error_response' do 63 | context 'without errors' do 64 | let(:resource) { { 'key' => 'value' } } 65 | let(:body) { { 'charge' => resource } } 66 | 67 | it 'returns empty string' do 68 | expect(subject.error_response).to eq('') 69 | end 70 | end 71 | 72 | context 'with error key' do 73 | let(:error_msg) { 'Error msg' } 74 | let(:body) { { 'error' => error_msg } } 75 | 76 | it 'returns error msg' do 77 | expect(subject.error_response).to eq(error_msg) 78 | end 79 | end 80 | 81 | context 'with errors key' do 82 | let(:error_msg) { 'Error msg' } 83 | let(:body) { { 'errors' => [{ 'message' => error_msg }] } } 84 | 85 | it 'returns error msg' do 86 | expect(subject.error_response).to eq(error_msg) 87 | end 88 | end 89 | end 90 | 91 | describe '#pagination_meta' do 92 | context 'with pageInfo present' do 93 | let(:pagination_values) { { 'has_next_page' => true } } 94 | let(:body) { { 'pageInfo' => pagination_values } } 95 | 96 | it 'returns pagination data' do 97 | expect(subject.pagination_meta).to eq(pagination_values) 98 | end 99 | end 100 | 101 | context 'without pageInfo' do 102 | it 'returns empty hash' do 103 | expect(subject.pagination_meta).to eq({}) 104 | end 105 | end 106 | end 107 | end 108 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/client_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/client' 4 | 5 | RSpec.describe Openpix::RubySdk::Client do 6 | let(:auth_token) { 'my-token' } 7 | 8 | subject { described_class.new(auth_token) } 9 | 10 | describe 'initialization' do 11 | let(:http_client_mock) { double } 12 | 13 | before { allow(Openpix::RubySdk::HttpClient).to receive(:instance).and_return(http_client_mock) } 14 | 15 | it 'initializes the Http client singleton instance with the auth token' do 16 | expect(http_client_mock).to receive(:initialize_http_client).with(auth_token) 17 | 18 | subject 19 | end 20 | end 21 | 22 | describe 'resources accessor methods' do 23 | let(:expected_classes) do 24 | [ 25 | 'Openpix::RubySdk::Resources::Charge', 26 | 'Openpix::RubySdk::Resources::Customer', 27 | 'Openpix::RubySdk::Resources::Payment', 28 | 'Openpix::RubySdk::Resources::Refund', 29 | 'Openpix::RubySdk::Resources::Subscription', 30 | 'Openpix::RubySdk::Resources::Webhook' 31 | ] 32 | end 33 | 34 | expected_resources = %w[ 35 | charges 36 | customers 37 | payments 38 | refunds 39 | subscriptions 40 | webhooks 41 | ] 42 | 43 | expected_resources.each_with_index do |resource, index| 44 | it "defines the #{resource} method" do 45 | expect { subject.send(resource) }.not_to raise_error 46 | end 47 | 48 | it "returns the #{resource} class instance" do 49 | expect(subject.send(resource).class.to_s).to eq(expected_classes[index]) 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/http_client_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/http_client' 4 | 5 | RSpec.describe Openpix::RubySdk::HttpClient do 6 | let(:auth_token) { 'my-token' } 7 | 8 | describe '.instance' do 9 | it 'returns the instance from HttpClient as a singleton' do 10 | first_instance = described_class.instance 11 | second_instance = described_class.instance 12 | 13 | expect(first_instance.object_id).to eq(second_instance.object_id) 14 | end 15 | end 16 | 17 | describe '#initialize_http_client' do 18 | let(:expected_url) { "#{described_class::BASE_URL}#{described_class::API_VERSION}" } 19 | let(:expected_headers) { { 'Authorization' => auth_token } } 20 | 21 | it 'initializes Faraday client with appropriate url and headers' do 22 | expect(Faraday).to receive(:new).with(url: expected_url, headers: expected_headers) 23 | 24 | described_class.instance.initialize_http_client(auth_token) 25 | end 26 | 27 | context 'with custom api url and version' do 28 | let(:base_url) { 'https://api.com' } 29 | let(:version) { 'v2' } 30 | let(:expected_url) { "#{base_url}#{version}" } 31 | let(:expected_headers) { { 'Authorization' => auth_token } } 32 | 33 | it 'initializes Faraday client with appropriate url and headers' do 34 | expect(Faraday).to receive(:new).with(url: expected_url, headers: expected_headers) 35 | 36 | described_class.instance.initialize_http_client(auth_token, base_url, version) 37 | end 38 | end 39 | end 40 | 41 | describe '#post' do 42 | let(:faraday_mock) { double } 43 | let(:resource) { 'charge' } 44 | let(:body) { {} } 45 | 46 | before do 47 | allow(Faraday).to receive(:new).and_return(faraday_mock) 48 | described_class.instance.initialize_http_client(auth_token) 49 | end 50 | 51 | it 'calls post method from faraday with resource' do 52 | expect(faraday_mock).to receive(:post).with(resource) 53 | 54 | described_class.instance.post(resource, body: body) 55 | end 56 | end 57 | 58 | describe '#get' do 59 | let(:faraday_mock) { double } 60 | let(:resource) { 'charge' } 61 | 62 | before do 63 | allow(Faraday).to receive(:new).and_return(faraday_mock) 64 | described_class.instance.initialize_http_client(auth_token) 65 | end 66 | 67 | it 'calls get method from faraday with resource' do 68 | expect(faraday_mock).to receive(:get).with(resource) 69 | 70 | described_class.instance.get(resource) 71 | end 72 | end 73 | 74 | describe '#delete' do 75 | let(:faraday_mock) { double } 76 | let(:resource) { 'charge' } 77 | 78 | before do 79 | allow(Faraday).to receive(:new).and_return(faraday_mock) 80 | described_class.instance.initialize_http_client(auth_token) 81 | end 82 | 83 | it 'calls delete method from faraday with resource' do 84 | expect(faraday_mock).to receive(:delete).with(resource) 85 | 86 | described_class.instance.delete(resource) 87 | end 88 | end 89 | end 90 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/charge_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'shared/savable_resource' 4 | require_relative 'shared/fetchable_resource' 5 | require_relative 'shared/findable_resource' 6 | require_relative 'shared/destroyable_resource' 7 | require 'openpix/ruby_sdk/resources/charge' 8 | 9 | RSpec.describe Openpix::RubySdk::Resources::Charge do 10 | savable_params = { 11 | resource_class: described_class, 12 | attrs: { 13 | 'correlationID' => '123', 14 | 'value' => 500 15 | }, 16 | body_response: { 17 | 'charge' => { 18 | 'status' => 'ACTIVE', 19 | 'value' => 500, 20 | 'correlationID' => '123' 21 | } 22 | }, 23 | error_response: { 24 | 'error' => 'error from API charge' 25 | } 26 | } 27 | it_behaves_like 'savable resource', savable_params 28 | 29 | fetchable_params = { 30 | resource_class: described_class, 31 | body_response: { 32 | 'charges' => [ 33 | { 34 | 'status' => 'ACTIVE', 35 | 'value' => 500, 36 | 'correlationID' => '123' 37 | } 38 | ] 39 | }, 40 | error_response: { 41 | 'error' => 'error from API charge' 42 | } 43 | } 44 | it_behaves_like 'fetchable resource', fetchable_params 45 | 46 | findable_params = { 47 | resource_class: described_class, 48 | body_response: { 49 | 'charge' => { 50 | 'status' => 'ACTIVE', 51 | 'value' => 500, 52 | 'correlationID' => '123' 53 | } 54 | }, 55 | error_response: { 56 | 'error' => 'error from API charge' 57 | } 58 | } 59 | it_behaves_like 'findable resource', findable_params 60 | 61 | destroyable_params = { 62 | resource_class: described_class, 63 | error_response: { 64 | 'error' => 'error from API charge' 65 | } 66 | } 67 | it_behaves_like 'destroyable resource', destroyable_params 68 | 69 | let(:customer) { nil } 70 | let(:attrs) do 71 | { 72 | 'correlationID' => '123', 73 | 'value' => 500, 74 | 'customer' => customer 75 | } 76 | end 77 | 78 | subject { described_class.new(double('http_client')) } 79 | 80 | it 'sets its url' do 81 | expect(subject.to_url).to eq('charge') 82 | end 83 | 84 | it 'defines its create attributes' do 85 | expect(subject.create_attributes).to eq(Openpix::RubySdk::Resources::Charge::ATTRS) 86 | end 87 | 88 | describe '#init_body' do 89 | it 'sets the attrs defined by the ATTRS constant' do 90 | subject.init_body(params: attrs) 91 | 92 | expect(subject.correlationID).to eq(attrs['correlationID']) 93 | expect(subject.value).to eq(attrs['value']) 94 | expect(subject.customer).to eq(attrs['customer']) 95 | expect(subject.comment).to eq(nil) 96 | end 97 | end 98 | 99 | describe '#to_body' do 100 | before { subject.init_body(params: attrs) } 101 | 102 | context 'without customer' do 103 | let(:expected_body) do 104 | { 105 | 'correlationID' => attrs['correlationID'], 106 | 'value' => attrs['value'] 107 | } 108 | end 109 | 110 | it 'parses other fields normally' do 111 | expect(subject.to_body).to eq(expected_body) 112 | end 113 | end 114 | 115 | context 'with empty hash customer' do 116 | let(:customer) { {} } 117 | let(:expected_body) do 118 | { 119 | 'correlationID' => attrs['correlationID'], 120 | 'value' => attrs['value'] 121 | } 122 | end 123 | 124 | it 'parses other fields normally' do 125 | expect(subject.to_body).to eq(expected_body) 126 | end 127 | end 128 | 129 | context 'with customer' do 130 | let(:address) { nil } 131 | let(:customer) do 132 | { 133 | name: 'My Name', 134 | taxID: '44406223412', 135 | address: address 136 | } 137 | end 138 | 139 | context 'without address' do 140 | let(:expected_body) do 141 | { 142 | 'correlationID' => attrs['correlationID'], 143 | 'value' => attrs['value'], 144 | 'customer' => { 145 | 'name' => customer[:name], 146 | 'taxID' => customer[:taxID] 147 | } 148 | } 149 | end 150 | 151 | it 'parses customer body normally' do 152 | expect(subject.to_body).to eq(expected_body) 153 | end 154 | end 155 | 156 | context 'with empty hash address' do 157 | let(:address) { {} } 158 | let(:expected_body) do 159 | { 160 | 'correlationID' => attrs['correlationID'], 161 | 'value' => attrs['value'], 162 | 'customer' => { 163 | 'name' => customer[:name], 164 | 'taxID' => customer[:taxID] 165 | } 166 | } 167 | end 168 | 169 | it 'parses customer body normally' do 170 | expect(subject.to_body).to eq(expected_body) 171 | end 172 | end 173 | 174 | context 'with address' do 175 | let(:address) do 176 | { 177 | country: 'Brasil', 178 | zipcode: '02145123', 179 | street: 'Rua minharua', 180 | number: 123 181 | } 182 | end 183 | let(:expected_body) do 184 | { 185 | 'correlationID' => attrs['correlationID'], 186 | 'value' => attrs['value'], 187 | 'customer' => { 188 | 'name' => customer[:name], 189 | 'taxID' => customer[:taxID], 190 | 'address' => { 191 | 'country' => address[:country], 192 | 'zipcode' => address[:zipcode], 193 | 'street' => address[:street], 194 | 'number' => address[:number] 195 | } 196 | } 197 | } 198 | end 199 | 200 | it 'parses customer and address body normally' do 201 | expect(subject.to_body).to eq(expected_body) 202 | end 203 | end 204 | end 205 | end 206 | 207 | describe '#add_additional_info' do 208 | before { subject.init_body(params: attrs) } 209 | 210 | it 'adds a new key value pair to the additional_info request body' do 211 | expect(subject.additionalInfo).to be_nil 212 | 213 | subject.add_additional_info('venda', 'shiba blocks toy') 214 | 215 | expect(subject.additionalInfo).to eq([{ 'key' => 'venda', 'value' => 'shiba blocks toy' }]) 216 | end 217 | end 218 | 219 | describe '#set_interests' do 220 | before { subject.init_body(params: attrs) } 221 | 222 | it 'sets the interests attr' do 223 | subject.set_interests(0.2) 224 | 225 | expect(subject.interests).to eq({ 'value' => 0.2 }) 226 | end 227 | end 228 | 229 | describe '#set_fines' do 230 | before { subject.init_body(params: attrs) } 231 | 232 | it 'sets the interests attr' do 233 | subject.set_fines(0.2) 234 | 235 | expect(subject.fines).to eq({ 'value' => 0.2 }) 236 | end 237 | end 238 | end 239 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/customer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'shared/savable_resource' 4 | require_relative 'shared/fetchable_resource' 5 | require_relative 'shared/findable_resource' 6 | require_relative 'shared/not_destroyable_resource' 7 | require 'openpix/ruby_sdk/resources/customer' 8 | 9 | RSpec.describe Openpix::RubySdk::Resources::Customer do 10 | savable_params = { 11 | resource_class: described_class, 12 | attrs: { 13 | 'name' => 'My Name', 14 | 'taxID' => '99812628000159' 15 | }, 16 | body_response: { 17 | 'customer' => { 18 | 'name' => 'My Name', 19 | 'taxID' => { 20 | 'taxID' => '99812628000159', 21 | 'type' => 'BR:CNPJ' 22 | } 23 | } 24 | }, 25 | error_response: { 26 | 'error' => 'error from API charge' 27 | } 28 | } 29 | it_behaves_like 'savable resource', savable_params 30 | 31 | fetchable_params = { 32 | resource_class: described_class, 33 | body_response: { 34 | 'customers' => [ 35 | { 36 | 'name' => 'My Name', 37 | 'taxID' => { 38 | 'taxID' => '99812628000159', 39 | 'type' => 'BR:CNPJ' 40 | } 41 | } 42 | ] 43 | }, 44 | error_response: { 45 | 'error' => 'error from API charge' 46 | } 47 | } 48 | it_behaves_like 'fetchable resource', fetchable_params 49 | 50 | findable_params = { 51 | resource_class: described_class, 52 | body_response: { 53 | 'customer' => { 54 | 'name' => 'My Name', 55 | 'taxID' => { 56 | 'taxID' => '99812628000159', 57 | 'type' => 'BR:CNPJ' 58 | } 59 | } 60 | }, 61 | error_response: { 62 | 'error' => 'error from API charge' 63 | } 64 | } 65 | it_behaves_like 'findable resource', findable_params 66 | 67 | it_behaves_like 'not destroyable resource', described_class 68 | 69 | let(:address) { nil } 70 | let(:attrs) do 71 | { 72 | 'name' => 'My Name', 73 | 'taxID' => '99812628000159', 74 | 'address' => address 75 | } 76 | end 77 | 78 | subject { described_class.new(double('http_client')) } 79 | 80 | it 'sets its url' do 81 | expect(subject.to_url).to eq('customer') 82 | end 83 | 84 | it 'defines its create attributes' do 85 | expect(subject.create_attributes).to eq(Openpix::RubySdk::Resources::Customer::ATTRS) 86 | end 87 | 88 | describe '#init_body' do 89 | it 'sets the attrs defined by the ATTRS constant' do 90 | subject.init_body(params: attrs) 91 | 92 | expect(subject.name).to eq(attrs['name']) 93 | expect(subject.taxID).to eq(attrs['taxID']) 94 | expect(subject.email).to eq(nil) 95 | end 96 | end 97 | 98 | describe '#to_body' do 99 | before { subject.init_body(params: attrs) } 100 | 101 | context 'without address' do 102 | let(:expected_body) do 103 | { 104 | 'name' => attrs['name'], 105 | 'taxID' => attrs['taxID'] 106 | } 107 | end 108 | 109 | it 'parses other fields normally' do 110 | expect(subject.to_body).to eq(expected_body) 111 | end 112 | end 113 | 114 | context 'with empty hash address' do 115 | let(:address) { {} } 116 | let(:expected_body) do 117 | { 118 | 'name' => attrs['name'], 119 | 'taxID' => attrs['taxID'] 120 | } 121 | end 122 | 123 | it 'parses other fields normally' do 124 | expect(subject.to_body).to eq(expected_body) 125 | end 126 | end 127 | 128 | context 'with address' do 129 | let(:address) do 130 | { 131 | country: 'Brasil', 132 | zipcode: '02145123', 133 | street: 'Rua minharua', 134 | number: 123 135 | } 136 | end 137 | let(:expected_body) do 138 | { 139 | 'name' => attrs['name'], 140 | 'taxID' => attrs['taxID'], 141 | 'address' => { 142 | 'country' => address[:country], 143 | 'zipcode' => address[:zipcode], 144 | 'street' => address[:street], 145 | 'number' => address[:number] 146 | } 147 | } 148 | end 149 | 150 | it 'parses customer and address body normally' do 151 | expect(subject.to_body).to eq(expected_body) 152 | end 153 | end 154 | end 155 | end 156 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/payment_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'shared/savable_resource' 4 | require_relative 'shared/fetchable_resource' 5 | require_relative 'shared/findable_resource' 6 | require_relative 'shared/not_destroyable_resource' 7 | require 'openpix/ruby_sdk/resources/payment' 8 | 9 | RSpec.describe Openpix::RubySdk::Resources::Payment do 10 | savable_params = { 11 | resource_class: described_class, 12 | attrs: { 13 | 'value' => 54_005, 14 | 'correlationID' => '1234', 15 | 'sourceAccountId' => '123' 16 | }, 17 | body_response: { 18 | 'payment' => { 19 | 'value' => 54_005, 20 | 'status' => 'CREATED', 21 | 'correlationID' => '1234', 22 | 'sourceAccountId' => '123' 23 | } 24 | }, 25 | error_response: { 26 | 'error' => 'error from API charge' 27 | } 28 | } 29 | it_behaves_like 'savable resource', savable_params 30 | 31 | fetchable_params = { 32 | resource_class: described_class, 33 | body_response: { 34 | 'customers' => [ 35 | { 36 | 'value' => 54_005, 37 | 'status' => 'CREATED', 38 | 'correlationID' => '1234', 39 | 'sourceAccountId' => '123' 40 | } 41 | ] 42 | }, 43 | error_response: { 44 | 'error' => 'error from API charge' 45 | } 46 | } 47 | it_behaves_like 'fetchable resource', fetchable_params 48 | 49 | findable_params = { 50 | resource_class: described_class, 51 | body_response: { 52 | 'customer' => { 53 | 'value' => 54_005, 54 | 'status' => 'CREATED', 55 | 'correlationID' => '1234', 56 | 'sourceAccountId' => '123' 57 | } 58 | }, 59 | error_response: { 60 | 'error' => 'error from API charge' 61 | } 62 | } 63 | it_behaves_like 'findable resource', findable_params 64 | 65 | it_behaves_like 'not destroyable resource', described_class 66 | 67 | let(:sourceAccountId) { nil } 68 | let(:attrs) do 69 | { 70 | 'value' => 54_005, 71 | 'correlationID' => '1234', 72 | 'sourceAccountId' => sourceAccountId 73 | } 74 | end 75 | 76 | subject { described_class.new(double('http_client')) } 77 | 78 | it 'sets its url' do 79 | expect(subject.to_url).to eq('payment') 80 | end 81 | 82 | it 'defines its create attributes' do 83 | expect(subject.create_attributes).to eq(Openpix::RubySdk::Resources::Payment::ATTRS) 84 | end 85 | 86 | describe '#init_body' do 87 | it 'sets the attrs defined by the ATTRS constant' do 88 | subject.init_body(params: attrs) 89 | 90 | expect(subject.value).to eq(attrs['value']) 91 | expect(subject.correlationID).to eq(attrs['correlationID']) 92 | expect(subject.sourceAccountId).to eq(sourceAccountId) 93 | expect(subject.comment).to eq(nil) 94 | end 95 | end 96 | 97 | describe '#to_body' do 98 | before { subject.init_body(params: attrs) } 99 | 100 | context 'without sourceAccountId' do 101 | let(:expected_body) do 102 | { 103 | 'value' => attrs['value'], 104 | 'correlationID' => attrs['correlationID'] 105 | } 106 | end 107 | 108 | it 'parses other fields normally' do 109 | expect(subject.to_body).to eq(expected_body) 110 | end 111 | end 112 | 113 | context 'with sourceAccountId' do 114 | let(:sourceAccountId) { '123' } 115 | let(:expected_body) do 116 | { 117 | 'value' => attrs['value'], 118 | 'correlationID' => attrs['correlationID'], 119 | 'sourceAccountId' => attrs['sourceAccountId'] 120 | } 121 | end 122 | 123 | it 'parses sourceAccountId as expected' do 124 | expect(subject.to_body).to eq(expected_body) 125 | end 126 | end 127 | end 128 | end 129 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/refund_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'shared/savable_resource' 4 | require_relative 'shared/fetchable_resource' 5 | require_relative 'shared/findable_resource' 6 | require_relative 'shared/not_destroyable_resource' 7 | require 'openpix/ruby_sdk/resources/refund' 8 | 9 | RSpec.describe Openpix::RubySdk::Resources::Refund do 10 | savable_params = { 11 | resource_class: described_class, 12 | attrs: { 13 | 'value' => 4000, 14 | 'correlationID' => '1234', 15 | 'transactionEndToEndId' => '123' 16 | }, 17 | body_response: { 18 | 'refund' => { 19 | 'value' => 4000, 20 | 'status' => 'IN_PROCESSING', 21 | 'correlationID' => '1234', 22 | 'sourceAccountId' => '123', 23 | 'time' => '2021-03-02T17:28:51.882Z' 24 | } 25 | }, 26 | error_response: { 27 | 'error' => 'error from API charge' 28 | } 29 | } 30 | it_behaves_like 'savable resource', savable_params 31 | 32 | fetchable_params = { 33 | resource_class: described_class, 34 | body_response: { 35 | 'refunds' => [ 36 | { 37 | 'value' => 4000, 38 | 'status' => 'IN_PROCESSING', 39 | 'correlationID' => '1234', 40 | 'sourceAccountId' => '123', 41 | 'time' => '2021-03-02T17:28:51.882Z' 42 | } 43 | ] 44 | }, 45 | error_response: { 46 | 'error' => 'error from API charge' 47 | } 48 | } 49 | it_behaves_like 'fetchable resource', fetchable_params 50 | 51 | findable_params = { 52 | resource_class: described_class, 53 | body_response: { 54 | 'refund' => { 55 | 'value' => 4000, 56 | 'status' => 'IN_PROCESSING', 57 | 'correlationID' => '1234', 58 | 'sourceAccountId' => '123', 59 | 'time' => '2021-03-02T17:28:51.882Z' 60 | } 61 | }, 62 | error_response: { 63 | 'error' => 'error from API charge' 64 | } 65 | } 66 | it_behaves_like 'findable resource', findable_params 67 | 68 | it_behaves_like 'not destroyable resource', described_class 69 | 70 | let(:transactionEndToEndId) { nil } 71 | let(:attrs) do 72 | { 73 | 'value' => 4000, 74 | 'correlationID' => '1234', 75 | 'transactionEndToEndId' => transactionEndToEndId 76 | } 77 | end 78 | 79 | subject { described_class.new(double('http_client')) } 80 | 81 | it 'sets its url' do 82 | expect(subject.to_url).to eq('refund') 83 | end 84 | 85 | it 'defines its create attributes' do 86 | expect(subject.create_attributes).to eq(Openpix::RubySdk::Resources::Refund::ATTRS) 87 | end 88 | 89 | describe '#init_body' do 90 | it 'sets the attrs defined by the ATTRS constant' do 91 | subject.init_body(params: attrs) 92 | 93 | expect(subject.value).to eq(attrs['value']) 94 | expect(subject.correlationID).to eq(attrs['correlationID']) 95 | expect(subject.transactionEndToEndId).to eq(transactionEndToEndId) 96 | expect(subject.comment).to eq(nil) 97 | end 98 | end 99 | 100 | describe '#to_body' do 101 | before { subject.init_body(params: attrs) } 102 | 103 | context 'without transactionEndToEndId' do 104 | let(:expected_body) do 105 | { 106 | 'value' => attrs['value'], 107 | 'correlationID' => attrs['correlationID'] 108 | } 109 | end 110 | 111 | it 'parses other fields normally' do 112 | expect(subject.to_body).to eq(expected_body) 113 | end 114 | end 115 | 116 | context 'with transactionEndToEndId' do 117 | let(:transactionEndToEndId) { '123' } 118 | let(:expected_body) do 119 | { 120 | 'value' => attrs['value'], 121 | 'correlationID' => attrs['correlationID'], 122 | 'transactionEndToEndId' => attrs['transactionEndToEndId'] 123 | } 124 | end 125 | 126 | it 'parses transactionEndToEndId as expected' do 127 | expect(subject.to_body).to eq(expected_body) 128 | end 129 | end 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/shared/destroyable_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'uri' 4 | 5 | RSpec.shared_examples 'destroyable resource' do |params| 6 | let(:mocked_http_client) { double('http_client') } 7 | let(:resource) { params[:resource_class].new(mocked_http_client) } 8 | let(:id) { 'a-id@' } 9 | let(:encoded_url) { "#{resource.to_url}/#{URI.encode_www_form_component(id)}" } 10 | let(:request_response) do 11 | Struct.new(:status, :body) do 12 | def initialize(status:, body: {}) 13 | super(status, body) 14 | end 15 | end 16 | end 17 | 18 | describe '#destroy' do 19 | it 'deletes the resource through http_client delete method' do 20 | expect(mocked_http_client).to receive(:delete).with( 21 | encoded_url, 22 | headers: {} 23 | ).and_return request_response.new( 24 | status: 200 25 | ) 26 | 27 | response = resource.destroy(id: id) 28 | 29 | expect(response.success?).to eq(true) 30 | end 31 | 32 | context 'with error response' do 33 | it 'returns success false and sets the error msg' do 34 | expect(mocked_http_client).to receive(:delete).with( 35 | encoded_url, 36 | headers: {} 37 | ).and_return request_response.new( 38 | status: 400, 39 | body: params[:error_response] 40 | ) 41 | 42 | response = resource.destroy(id: id) 43 | 44 | expect(response.success?).to eq(false) 45 | expect(response.error_response).to eq(params[:error_response]['error']) 46 | end 47 | end 48 | end 49 | 50 | describe '#destroy!' do 51 | it 'deletes the resource through http_client delete method' do 52 | expect(mocked_http_client).to receive(:delete).with( 53 | encoded_url, 54 | headers: {} 55 | ).and_return request_response.new( 56 | status: 200 57 | ) 58 | 59 | response = resource.destroy!(id: id) 60 | 61 | expect(response.success?).to eq(true) 62 | end 63 | 64 | context 'with error response' do 65 | it 'raises an error' do 66 | expect(mocked_http_client).to receive(:delete).with( 67 | encoded_url, 68 | headers: {} 69 | ).and_return request_response.new( 70 | status: 400, 71 | body: params[:error_response] 72 | ) 73 | 74 | expect { resource.destroy!(id: id) }.to raise_error(Openpix::RubySdk::Resources::RequestError) 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/shared/fetchable_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'active_support' 4 | require 'active_support/core_ext/string/inflections' 5 | 6 | RSpec.shared_examples 'fetchable resource' do |params| 7 | let(:mocked_http_client) { double('http_client') } 8 | let(:resource) { params[:resource_class].new(mocked_http_client) } 9 | let(:request_response) do 10 | Struct.new(:status, :body) do 11 | def initialize(status:, body: {}) 12 | super(status, body) 13 | end 14 | end 15 | end 16 | 17 | describe '#fetch' do 18 | it 'fetches resources through http_client get method' do 19 | expect(mocked_http_client).to receive(:get).with( 20 | resource.to_url, 21 | headers: {}, 22 | params: { skip: 0, limit: 100 } 23 | ).and_return request_response.new( 24 | status: 200, 25 | body: { 26 | 'pageInfo' => { 27 | 'totalCount' => 10, 28 | 'hasPreviousPage' => false, 29 | 'hasNextPage' => false 30 | } 31 | }.merge(params[:body_response]) 32 | ) 33 | 34 | response = resource.fetch 35 | 36 | expect(response.success?).to eq(true) 37 | expect(response.resource_response).to eq(params[:body_response][resource.to_url.pluralize]) 38 | end 39 | 40 | context 'with error response' do 41 | it 'returns success false and shows the error message' do 42 | expect(mocked_http_client).to receive(:get).with( 43 | resource.to_url, 44 | headers: {}, 45 | params: { skip: 0, limit: 100 } 46 | ).and_return request_response.new( 47 | status: 400, 48 | body: params[:error_response] 49 | ) 50 | 51 | response = resource.fetch 52 | 53 | expect(response.success?).to eq(false) 54 | expect(response.error_response).to eq(params[:error_response]['error']) 55 | end 56 | end 57 | end 58 | 59 | describe '#fetch!' do 60 | it 'fetches resources through http_client get method' do 61 | expect(mocked_http_client).to receive(:get).with( 62 | resource.to_url, 63 | headers: {}, 64 | params: { skip: 0, limit: 100 } 65 | ).and_return request_response.new( 66 | status: 200, 67 | body: { 68 | 'pageInfo' => { 69 | 'totalCount' => 10, 70 | 'hasPreviousPage' => false, 71 | 'hasNextPage' => false 72 | } 73 | }.merge(params[:body_response]) 74 | ) 75 | 76 | response = resource.fetch! 77 | 78 | expect(response.success?).to eq(true) 79 | expect(response.resource_response).to eq(params[:body_response][resource.to_url.pluralize]) 80 | end 81 | 82 | context 'with error response' do 83 | it 'raises an error' do 84 | expect(mocked_http_client).to receive(:get).with( 85 | resource.to_url, 86 | headers: {}, 87 | params: { skip: 0, limit: 100 } 88 | ).and_return request_response.new( 89 | status: 400, 90 | body: params[:error_response] 91 | ) 92 | 93 | expect { resource.fetch! }.to raise_error(Openpix::RubySdk::Resources::RequestError) 94 | end 95 | end 96 | end 97 | 98 | describe '#fetch_next_page!' do 99 | context 'with fetch being called before' do 100 | let(:limit) { 5 } 101 | before do 102 | expect(mocked_http_client).to receive(:get).with( 103 | resource.to_url, 104 | headers: {}, 105 | params: { skip: 0, limit: limit } 106 | ).and_return request_response.new( 107 | status: 200, 108 | body: { 109 | 'pageInfo' => { 110 | 'totalCount' => 10, 111 | 'hasPreviousPage' => more_pages, 112 | 'hasNextPage' => more_pages 113 | } 114 | }.merge(params[:body_response]) 115 | ) 116 | 117 | resource.fetch(limit: limit) 118 | end 119 | 120 | context 'having more pages' do 121 | let(:more_pages) { true } 122 | 123 | it 'fetches next page through http_client get method' do 124 | expect(mocked_http_client).to receive(:get).with( 125 | resource.to_url, 126 | headers: {}, 127 | params: { skip: limit, limit: limit } 128 | ).and_return request_response.new( 129 | status: 200, 130 | body: { 131 | 'pageInfo' => { 132 | 'totalCount' => 10, 133 | 'hasPreviousPage' => more_pages, 134 | 'hasNextPage' => more_pages 135 | } 136 | }.merge(params[:body_response]) 137 | ) 138 | 139 | response = resource.fetch_next_page! 140 | 141 | expect(response.success?).to eq(true) 142 | expect(response.resource_response).to eq(params[:body_response][resource.to_url.pluralize]) 143 | end 144 | end 145 | 146 | context 'without more pages' do 147 | let(:more_pages) { false } 148 | 149 | it 'raises an error' do 150 | expect { resource.fetch_next_page! }.to raise_error(Openpix::RubySdk::Resources::PageNotDefinedError) 151 | end 152 | end 153 | end 154 | 155 | context 'without fetch being called before' do 156 | it 'raises an error' do 157 | expect { resource.fetch_next_page! }.to raise_error(Openpix::RubySdk::Resources::NotFetchedError) 158 | end 159 | end 160 | end 161 | 162 | describe '#fetch_previous_page!' do 163 | context 'with fetch being called before' do 164 | let(:limit) { 5 } 165 | let(:skip) { 10 } 166 | before do 167 | expect(mocked_http_client).to receive(:get).with( 168 | resource.to_url, 169 | headers: {}, 170 | params: { skip: skip, limit: limit } 171 | ).and_return request_response.new( 172 | status: 200, 173 | body: { 174 | 'pageInfo' => { 175 | 'totalCount' => 50, 176 | 'hasPreviousPage' => more_pages, 177 | 'hasNextPage' => more_pages 178 | } 179 | }.merge(params[:body_response]) 180 | ) 181 | 182 | resource.fetch(skip: skip, limit: limit) 183 | end 184 | 185 | context 'having more pages' do 186 | let(:more_pages) { true } 187 | 188 | it 'fetches previous page through http_client get method' do 189 | expect(mocked_http_client).to receive(:get).with( 190 | resource.to_url, 191 | headers: {}, 192 | params: { skip: (skip - limit), limit: limit } 193 | ).and_return request_response.new( 194 | status: 200, 195 | body: { 196 | 'pageInfo' => { 197 | 'totalCount' => 50, 198 | 'hasPreviousPage' => more_pages, 199 | 'hasNextPage' => more_pages 200 | } 201 | }.merge(params[:body_response]) 202 | ) 203 | 204 | response = resource.fetch_previous_page! 205 | 206 | expect(response.success?).to eq(true) 207 | expect(response.resource_response).to eq(params[:body_response][resource.to_url.pluralize]) 208 | end 209 | end 210 | 211 | context 'without more pages' do 212 | let(:more_pages) { false } 213 | 214 | it 'raises an error' do 215 | expect { resource.fetch_previous_page! }.to raise_error(Openpix::RubySdk::Resources::PageNotDefinedError) 216 | end 217 | end 218 | end 219 | 220 | context 'without fetch being called before' do 221 | it 'raises an error' do 222 | expect { resource.fetch_previous_page! }.to raise_error(Openpix::RubySdk::Resources::NotFetchedError) 223 | end 224 | end 225 | end 226 | end 227 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/shared/findable_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'uri' 4 | 5 | RSpec.shared_examples 'findable resource' do |params| 6 | let(:mocked_http_client) { double('http_client') } 7 | let(:resource) { params[:resource_class].new(mocked_http_client) } 8 | let(:id) { 'a-id@' } 9 | let(:encoded_url) { "#{resource.to_url}/#{URI.encode_www_form_component(id)}" } 10 | let(:request_response) do 11 | Struct.new(:status, :body) do 12 | def initialize(status:, body: {}) 13 | super(status, body) 14 | end 15 | end 16 | end 17 | 18 | describe '#find' do 19 | it 'finds the resource through http_client get method' do 20 | expect(mocked_http_client).to receive(:get).with( 21 | encoded_url, 22 | headers: {}, 23 | params: {} 24 | ).and_return request_response.new( 25 | status: 200, 26 | body: params[:body_response] 27 | ) 28 | 29 | response = resource.find(id: id) 30 | 31 | expect(response.success?).to eq(true) 32 | expect(response.resource_response).to eq(params[:body_response][resource.to_single_resource]) 33 | end 34 | 35 | context 'with error response' do 36 | it 'returns success false and sets the error msg' do 37 | expect(mocked_http_client).to receive(:get).with( 38 | encoded_url, 39 | headers: {}, 40 | params: {} 41 | ).and_return request_response.new( 42 | status: 400, 43 | body: params[:error_response] 44 | ) 45 | 46 | response = resource.find(id: id) 47 | 48 | expect(response.success?).to eq(false) 49 | expect(response.error_response).to eq(params[:error_response]['error']) 50 | end 51 | end 52 | end 53 | 54 | describe '#find!' do 55 | it 'finds the resource through http_client get method' do 56 | expect(mocked_http_client).to receive(:get).with( 57 | encoded_url, 58 | headers: {}, 59 | params: {} 60 | ).and_return request_response.new( 61 | status: 200, 62 | body: params[:body_response] 63 | ) 64 | 65 | response = resource.find!(id: id) 66 | 67 | expect(response.success?).to eq(true) 68 | expect(response.resource_response).to eq(params[:body_response][resource.to_single_resource]) 69 | end 70 | 71 | context 'with error response' do 72 | it 'raises an error' do 73 | expect(mocked_http_client).to receive(:get).with( 74 | encoded_url, 75 | headers: {}, 76 | params: {} 77 | ).and_return request_response.new( 78 | status: 400, 79 | body: params[:error_response] 80 | ) 81 | 82 | expect { resource.find!(id: id) }.to raise_error(Openpix::RubySdk::Resources::RequestError) 83 | end 84 | end 85 | end 86 | end 87 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/shared/not_destroyable_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.shared_examples 'not destroyable resource' do |resource_class| 4 | let(:resource) { resource_class.new(double('http_client')) } 5 | let(:id) { 'some-id' } 6 | 7 | describe '#destroy' do 8 | it 'raises an error' do 9 | expect { resource.destroy(id: id) }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 10 | end 11 | end 12 | 13 | describe '#destroy!' do 14 | it 'raises an error' do 15 | expect { resource.destroy!(id: id) }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/shared/not_fetchable_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.shared_examples 'not fetchable resource' do |resource_class| 4 | let(:resource) { resource_class.new(double('http_client')) } 5 | let(:id) { 'some-id' } 6 | 7 | describe '#fetch' do 8 | it 'raises an error' do 9 | expect { resource.fetch }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 10 | end 11 | end 12 | 13 | describe '#fetch!' do 14 | it 'raises an error' do 15 | expect { resource.fetch! }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 16 | end 17 | end 18 | 19 | describe '#fetch_next_page!' do 20 | it 'raises an error' do 21 | expect { resource.fetch_next_page! }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 22 | end 23 | end 24 | 25 | describe '#fetch_previous_page!' do 26 | it 'raises an error' do 27 | expect { resource.fetch_previous_page! }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/shared/not_findable_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.shared_examples 'not findable resource' do |resource_class| 4 | let(:resource) { resource_class.new(double('http_client')) } 5 | let(:id) { 'some-id' } 6 | 7 | describe '#find' do 8 | it 'raises an error' do 9 | expect { resource.find(id: id) }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 10 | end 11 | end 12 | 13 | describe '#find!' do 14 | it 'raises an error' do 15 | expect { resource.find!(id: id) }.to raise_error(Openpix::RubySdk::Resources::ActionNotImplementedError) 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/shared/savable_resource.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | RSpec.shared_examples 'savable resource' do |params| 4 | let(:mocked_http_client) { double('http_client') } 5 | let(:resource) { params[:resource_class].new(mocked_http_client) } 6 | let(:request_response) do 7 | Struct.new(:status, :body) do 8 | def initialize(status:, body: {}) 9 | super(status, body) 10 | end 11 | end 12 | end 13 | 14 | before { resource.init_body(params: params[:attrs]) } 15 | 16 | describe '#save' do 17 | it 'saves the resource through http_client post method' do 18 | expect(mocked_http_client).to receive(:post).with( 19 | resource.to_url, 20 | body: resource.to_body, 21 | headers: {}, 22 | params: { return_existing: false } 23 | ).and_return request_response.new( 24 | status: 200, 25 | body: params[:body_response] 26 | ) 27 | 28 | response = resource.save 29 | 30 | expect(response.success?).to eq(true) 31 | expect(response.resource_response).to eq(params[:body_response][resource.to_single_resource]) 32 | end 33 | 34 | context 'with error response' do 35 | it 'returns success false and sets the error msg' do 36 | expect(mocked_http_client).to receive(:post).with( 37 | resource.to_url, 38 | body: resource.to_body, 39 | headers: {}, 40 | params: { return_existing: false } 41 | ).and_return request_response.new( 42 | status: 400, 43 | body: params[:error_response] 44 | ) 45 | 46 | response = resource.save 47 | 48 | expect(response.success?).to eq(false) 49 | expect(response.error_response).to eq(params[:error_response]['error']) 50 | end 51 | end 52 | end 53 | 54 | describe '#save!' do 55 | it 'saves the resource through http_client post method' do 56 | expect(mocked_http_client).to receive(:post).with( 57 | resource.to_url, 58 | body: resource.to_body, 59 | headers: {}, 60 | params: { return_existing: false } 61 | ).and_return request_response.new( 62 | status: 200, 63 | body: params[:body_response] 64 | ) 65 | 66 | response = resource.save! 67 | 68 | expect(response.success?).to eq(true) 69 | expect(response.resource_response).to eq(params[:body_response][resource.to_single_resource]) 70 | end 71 | 72 | context 'with error response' do 73 | it 'raises an error' do 74 | expect(mocked_http_client).to receive(:post).with( 75 | resource.to_url, 76 | body: resource.to_body, 77 | headers: {}, 78 | params: { return_existing: false } 79 | ).and_return request_response.new( 80 | status: 400, 81 | body: params[:error_response] 82 | ) 83 | 84 | expect { resource.save! }.to raise_error(Openpix::RubySdk::Resources::RequestError) 85 | end 86 | end 87 | end 88 | end 89 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/subscription_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'shared/savable_resource' 4 | require_relative 'shared/findable_resource' 5 | require_relative 'shared/not_destroyable_resource' 6 | require_relative 'shared/not_fetchable_resource' 7 | require 'openpix/ruby_sdk/resources/subscription' 8 | 9 | RSpec.describe Openpix::RubySdk::Resources::Subscription do 10 | savable_params = { 11 | resource_class: described_class, 12 | attrs: { 13 | 'value' => 4000, 14 | 'customer' => { 15 | 'name' => 'Customer Name', 16 | 'taxID' => '31324227036' 17 | } 18 | }, 19 | body_response: { 20 | 'subscription' => { 21 | 'globalID' => 'UGF5bWVudFN1YnNjcmlwdGlvbjo2M2UzYjJiNzczZDNkOTNiY2RkMzI5OTM=', 22 | 'value' => 4000, 23 | 'dayGenerateCharge' => 5, 24 | 'customer' => { 25 | 'name' => 'Customer Name', 26 | 'taxID' => { 27 | 'taxID' => '31324227036', 28 | 'type' => 'BR:CPF' 29 | } 30 | } 31 | } 32 | }, 33 | error_response: { 34 | 'error' => 'error from API charge' 35 | } 36 | } 37 | it_behaves_like 'savable resource', savable_params 38 | 39 | findable_params = { 40 | resource_class: described_class, 41 | body_response: { 42 | 'subscription' => { 43 | 'globalID' => 'UGF5bWVudFN1YnNjcmlwdGlvbjo2M2UzYjJiNzczZDNkOTNiY2RkMzI5OTM=', 44 | 'value' => 4000, 45 | 'dayGenerateCharge' => 5, 46 | 'customer' => { 47 | 'name' => 'Customer Name', 48 | 'taxID' => { 49 | 'taxID' => '31324227036', 50 | 'type' => 'BR:CPF' 51 | } 52 | } 53 | } 54 | }, 55 | error_response: { 56 | 'error' => 'error from API charge' 57 | } 58 | } 59 | it_behaves_like 'findable resource', findable_params 60 | 61 | it_behaves_like 'not destroyable resource', described_class 62 | it_behaves_like 'not fetchable resource', described_class 63 | 64 | let(:attrs) do 65 | { 66 | 'value' => 4000, 67 | 'customer' => { 68 | 'name' => 'Customer Name', 69 | 'taxID' => '31324227036' 70 | } 71 | } 72 | end 73 | 74 | subject { described_class.new(double('http_client')) } 75 | 76 | it 'sets its url' do 77 | expect(subject.to_url).to eq('subscriptions') 78 | end 79 | 80 | it 'defines its create attributes' do 81 | expect(subject.create_attributes).to eq(Openpix::RubySdk::Resources::Subscription::ATTRS) 82 | end 83 | 84 | describe '#init_body' do 85 | it 'sets the attrs defined by the ATTRS constant' do 86 | subject.init_body(params: attrs) 87 | 88 | expect(subject.value).to eq(attrs['value']) 89 | expect(subject.customer).to eq(attrs['customer']) 90 | expect(subject.dayGenerateCharge).to eq(nil) 91 | end 92 | end 93 | 94 | describe '#to_body' do 95 | before { subject.init_body(params: attrs) } 96 | 97 | let(:expected_body) do 98 | { 99 | 'value' => attrs['value'], 100 | 'customer' => { 101 | 'name' => attrs['customer']['name'], 102 | 'taxID' => attrs['customer']['taxID'] 103 | } 104 | } 105 | end 106 | 107 | it 'parses all the fields correctly' do 108 | expect(subject.to_body).to eq(expected_body) 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/resources/webhook_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'shared/savable_resource' 4 | require_relative 'shared/fetchable_resource' 5 | require_relative 'shared/destroyable_resource' 6 | require_relative 'shared/not_findable_resource' 7 | require 'openpix/ruby_sdk/resources/webhook' 8 | 9 | RSpec.describe Openpix::RubySdk::Resources::Webhook do 10 | savable_params = { 11 | resource_class: described_class, 12 | attrs: { 13 | 'name' => 'my webhook create', 14 | 'event' => 'OPENPIX:CHARGE_CREATED', 15 | 'url' => 'https://mycompany.com.br/webhook', 16 | 'authorization' => 'openpix', 17 | 'isActive' => false 18 | }, 19 | body_response: { 20 | 'webhook' => { 21 | 'id' => 'V2ViaG9vazo2MDNlYmUxZWRlYjkzNWU4NmQyMmNmMTg=', 22 | 'name' => 'my webhook create', 23 | 'url' => 'https://mycompany.com.br/webhook', 24 | 'authorization' => 'openpix', 25 | 'isActive' => false, 26 | 'event' => 'OPENPIX:CHARGE_CREATED', 27 | 'createdAt' => '2021-03-02T22:29:10.720Z', 28 | 'updatedAt' => '2021-03-02T22:29:10.720Z' 29 | } 30 | }, 31 | error_response: { 32 | 'error' => 'error from API charge' 33 | } 34 | } 35 | it_behaves_like 'savable resource', savable_params 36 | 37 | fetchable_params = { 38 | resource_class: described_class, 39 | body_response: { 40 | 'webhooks' => [ 41 | { 42 | 'id' => 'V2ViaG9vazo2MDNlYmUxZWRlYjkzNWU4NmQyMmNmMTg=', 43 | 'name' => 'my webhook create', 44 | 'url' => 'https://mycompany.com.br/webhook', 45 | 'authorization' => 'openpix', 46 | 'isActive' => false, 47 | 'event' => 'OPENPIX:CHARGE_CREATED', 48 | 'createdAt' => '2021-03-02T22:29:10.720Z', 49 | 'updatedAt' => '2021-03-02T22:29:10.720Z' 50 | } 51 | ] 52 | }, 53 | error_response: { 54 | 'error' => 'error from API charge' 55 | } 56 | } 57 | it_behaves_like 'fetchable resource', fetchable_params 58 | 59 | destroyable_params = { 60 | resource_class: described_class, 61 | error_response: { 62 | 'error' => 'error from API charge' 63 | } 64 | } 65 | it_behaves_like 'destroyable resource', destroyable_params 66 | 67 | it_behaves_like 'not findable resource', described_class 68 | 69 | let(:attrs) do 70 | { 71 | 'name' => 'my webhook create', 72 | 'event' => 'OPENPIX:CHARGE_CREATED', 73 | 'url' => 'https://mycompany.com.br/webhook', 74 | 'authorization' => 'openpix', 75 | 'isActive' => false 76 | } 77 | end 78 | 79 | subject { described_class.new(double('http_client')) } 80 | 81 | it 'sets its url' do 82 | expect(subject.to_url).to eq('webhook') 83 | end 84 | 85 | it 'defines its create attributes' do 86 | expect(subject.create_attributes).to eq(Openpix::RubySdk::Resources::Webhook::ATTRS) 87 | end 88 | 89 | describe '#init_body' do 90 | it 'sets the attrs defined by the ATTRS constant' do 91 | subject.init_body(params: attrs) 92 | 93 | expect(subject.name).to eq(attrs['name']) 94 | expect(subject.event).to eq(attrs['event']) 95 | expect(subject.url).to eq(attrs['url']) 96 | expect(subject.authorization).to eq(attrs['authorization']) 97 | expect(subject.isActive).to eq(attrs['isActive']) 98 | end 99 | end 100 | 101 | describe '#to_body' do 102 | before { subject.init_body(params: attrs) } 103 | 104 | let(:expected_body) do 105 | { 106 | webhook: { 107 | 'name' => attrs['name'], 108 | 'url' => attrs['url'], 109 | 'authorization' => attrs['authorization'], 110 | 'isActive' => attrs['isActive'], 111 | 'event' => attrs['event'] 112 | } 113 | } 114 | end 115 | 116 | it 'parses all fields as expected' do 117 | expect(subject.to_body).to eq(expected_body) 118 | end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk/utils_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk/utils' 4 | 5 | RSpec.describe Openpix::RubySdk::Utils do 6 | describe '.verify_signature' do 7 | let(:payload) do 8 | '{ "pixQrCode": null, "charge": { "status": "COMPLETED", "customer": { "name": "Antonio Victor", "taxID": { "taxID": "12345678976", "type": "BR:CPF" }, "email": "antoniocliente@example.com", "correlationID": "4979ceba-2132-4292-bd90-bee7fb2125e4" }, "value": 1000, "comment": "Pagamento OpenPix", "transactionID": "ea83401ed4834b3ea6f1f283b389af29", "correlationID": "417bae21-3d08-4cdb-9c2d-fee63c89e9e4", "paymentLinkID": "34697ed2-3790-4b60-8512-e7465b142d84", "createdAt": "2021-03-12T12:43:54.528Z", "updatedAt": "2021-03-12T12:44:09.360Z", "brCode": "https://api.openpix.com.br/openpix/openpix/testing?transactionID=ea83401ed4834b3ea6f1f283b389af29" }, "pix": { "charge": { "status": "COMPLETED", "customer": { "name": "Antonio Victor", "taxID": { "taxID": "12345678976", "type": "BR:CPF" }, "email": "antoniocliente@example.com", "correlationID": "4979ceba-2132-4292-bd90-bee7fb2125e4" }, "value": 1000, "comment": "Pagamento OpenPix", "transactionID": "ea83401ed4834b3ea6f1f283b389af29", "correlationID": "417bae21-3d08-4cdb-9c2d-fee63c89e9e4", "paymentLinkID": "34697ed2-3790-4b60-8512-e7465b142d84", "createdAt": "2021-03-12T12:43:54.528Z", "updatedAt": "2021-03-12T12:44:09.360Z" }, "customer": { "correlationID": "9134e286-6f71-427a-bf00-241681624586", "email": "email1@example.com", "name": "Loma", "phone": "+5511999999999", "taxID": { "taxID": "47043622050", "type": "BR:CPF" } }, "payer": { "correlationID": "9134e286-6f71-427a-bf00-241681624586", "email": "email1@example.com", "name": "Loma", "phone": "+5511999999999", "taxID": { "taxID": "47043622050", "type": "BR:CPF" } }, "time": "2021-03-12T12:44:09.269Z", "value": 1, "transactionID": "ea83401ed4834b3ea6f1f283b389af29", "infoPagador": "OpenPix testing" }, "company": { "id": "624f46f9e93f9f521c8308d7", "name": "Pizzaria do José", "taxID": "4722767300014" }, "account": { "clientId": "ZOJ64B9B-ZM1W-89MI-4UCI-OP2LVIU6NY75" } }' 9 | end 10 | let(:base64_signature) do 11 | 'lL2nnXgmLFGgxJ8+jCDguqouU4ucrIxYJcU5SPrJFaNcJajTJHYVldqc/z4YFIjAjtPEALe699WosgPY08W7CLpidvtm06Qwa4YMB0l/DcTS93O91NdSH/adjugEKiOb76Zj/0jB8mqOmWCFYbweOBa17bssuEkd5Lw7Q5L314Y=' 12 | end 13 | 14 | it 'returns true for valid signature' do 15 | expect(described_class.verify_signature(base64_signature, payload)).to eq(true) 16 | end 17 | 18 | context 'with invalid signature' do 19 | let(:payload) { '{ "invalid": "payload" }' } 20 | 21 | it 'returns false' do 22 | expect(described_class.verify_signature(base64_signature, payload)).to eq(false) 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/openpix/ruby_sdk_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'openpix/ruby_sdk' 4 | 5 | RSpec.describe Openpix::RubySdk do 6 | it 'has a version number' do 7 | expect(Openpix::RubySdk::VERSION).not_to be nil 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'pry' 4 | 5 | RSpec.configure do |config| 6 | # Enable flags like --only-failures and --next-failure 7 | config.example_status_persistence_file_path = '.rspec_status' 8 | 9 | # Disable RSpec exposing methods globally on `Module` and `main` 10 | config.disable_monkey_patching! 11 | 12 | config.expect_with :rspec do |c| 13 | c.syntax = :expect 14 | end 15 | 16 | config.order = :random 17 | end 18 | --------------------------------------------------------------------------------