├── .circleci └── config.yml ├── .codecov.yml ├── .github ├── ISSUE_TEMPLATE │ ├── bug-report.md │ ├── documentation.md │ ├── feature-request.md │ └── general-issue.md ├── pull_request_template.md └── stale.yml ├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── CHANGELOG.md ├── CODE-OF-CONDUCT.md ├── GENERAL-CONTRIBUTING.md ├── LICENSE ├── README.md ├── SAMPLE-CODE.md ├── icon.png ├── java-sdk-airtime ├── .gitignore ├── USAGE.md ├── pom.xml ├── src │ ├── main │ │ └── java │ │ │ └── software │ │ │ └── reloadly │ │ │ └── sdk │ │ │ └── airtime │ │ │ ├── client │ │ │ └── AirtimeAPI.java │ │ │ ├── dto │ │ │ ├── Phone.java │ │ │ ├── request │ │ │ │ ├── EmailTopupRequest.java │ │ │ │ ├── PhoneTopupRequest.java │ │ │ │ ├── TopupRequest.java │ │ │ │ └── Topupable.java │ │ │ └── response │ │ │ │ ├── AccountBalanceInfo.java │ │ │ │ ├── AirtimeTransactionStatusResponse.java │ │ │ │ ├── AsyncAirtimeResponse.java │ │ │ │ ├── Country.java │ │ │ │ ├── Discount.java │ │ │ │ ├── FxRate.java │ │ │ │ ├── GeographicalRechargePlan.java │ │ │ │ ├── Operator.java │ │ │ │ ├── OperatorFxRate.java │ │ │ │ ├── PinDetail.java │ │ │ │ ├── Promotion.java │ │ │ │ ├── SimplifiedCountry.java │ │ │ │ ├── SimplifiedOperator.java │ │ │ │ ├── TopupTransaction.java │ │ │ │ └── TransactionBalanceInfo.java │ │ │ ├── enums │ │ │ ├── AirtimeTransactionStatus.java │ │ │ └── DenominationType.java │ │ │ ├── filter │ │ │ ├── OperatorFilter.java │ │ │ └── TransactionHistoryFilter.java │ │ │ ├── internal │ │ │ └── dto │ │ │ │ └── request │ │ │ │ └── FxRateRequest.java │ │ │ └── operation │ │ │ ├── AccountOperations.java │ │ │ ├── BaseAirtimeOperation.java │ │ │ ├── CountryOperations.java │ │ │ ├── DiscountOperations.java │ │ │ ├── OperatorOperations.java │ │ │ ├── PromotionOperations.java │ │ │ ├── ReportOperations.java │ │ │ ├── TopupOperations.java │ │ │ └── TransactionHistoryOperations.java │ └── test │ │ ├── java │ │ └── software │ │ │ └── reloadly │ │ │ └── sdk │ │ │ └── airtime │ │ │ ├── AsserterTest.java │ │ │ ├── ExceptionUtilTest.java │ │ │ ├── client │ │ │ ├── integration │ │ │ │ └── AirtimeAPITest.java │ │ │ └── unit │ │ │ │ └── AirtimeAPITest.java │ │ │ ├── interfaces │ │ │ ├── IntegrationTest.java │ │ │ └── IntegrationTestWithProxy.java │ │ │ ├── operation │ │ │ ├── integration │ │ │ │ ├── AccountOperationsTest.java │ │ │ │ ├── BaseIntegrationTest.java │ │ │ │ ├── CountryOperationsTest.java │ │ │ │ ├── DiscountOperationsTest.java │ │ │ │ ├── OperatorOperationsTest.java │ │ │ │ ├── PromotionOperationsTest.java │ │ │ │ ├── TopupOperationsTest.java │ │ │ │ └── TransactionHistoryOperationsTest.java │ │ │ └── unit │ │ │ │ ├── AccountOperationsTest.java │ │ │ │ ├── CountryOperationsTest.java │ │ │ │ ├── DiscountOperationsTest.java │ │ │ │ ├── OperatorOperationsTest.java │ │ │ │ ├── PromotionOperationsTest.java │ │ │ │ ├── TopupOperationsTest.java │ │ │ │ └── TransactionHistoryOperationsTest.java │ │ │ └── util │ │ │ ├── AirtimeAPIMockServer.java │ │ │ └── ExpiredToken.java │ │ └── resources │ │ ├── account │ │ └── account_balance.json │ │ ├── country │ │ ├── country.json │ │ └── country_list.json │ │ ├── discount │ │ ├── discount.json │ │ └── discount_page.json │ │ ├── operator │ │ ├── operator_auto_detect_filtered.json │ │ ├── operator_auto_detect_unfiltered.json │ │ ├── operator_filtered_response.json │ │ ├── operator_fx_rate.json │ │ ├── operator_unfiltered_response.json │ │ ├── operator_unfiltered_with_geographical_recharge_plans.json │ │ ├── operators_by_country_code_filtered_exclude_bundles.json │ │ ├── operators_by_country_code_unfiltered.json │ │ └── operators_paged_unfiltered_response.json │ │ ├── promotion │ │ ├── promotion_by_id.json │ │ ├── promotion_filtered_page.json │ │ ├── promotion_list_by_country.json │ │ ├── promotion_list_by_operator.json │ │ └── promotion_unfiltered_page.json │ │ ├── report │ │ ├── transaction_history_by_id.json │ │ ├── transaction_history_filtered_page.json │ │ └── transaction_history_unfiltered_page.json │ │ └── topup │ │ ├── email_topup_transaction.json │ │ ├── email_topup_transaction_async.json │ │ ├── email_topup_transaction_status.json │ │ ├── phone_topup_transaction.json │ │ ├── phone_topup_transaction_async.json │ │ ├── phone_topup_transaction_status.json │ │ └── phone_topup_transaction_with_pin_detail.json └── usage │ ├── ACCOUNT-OPERATIONS.md │ ├── COUNTRY-OPERATIONS.md │ ├── DISCOUNT-OPERATIONS.md │ ├── OPERATOR-OPERATIONS.md │ ├── PROMOTION-OPERATIONS.md │ ├── REPORT-OPERATIONS.md │ └── TOPUP-OPERATIONS.md ├── java-sdk-authentication ├── .gitignore ├── USAGE.md ├── pom.xml └── src │ ├── main │ └── java │ │ └── software │ │ └── reloadly │ │ └── sdk │ │ └── authentication │ │ ├── client │ │ ├── AuthenticationAPI.java │ │ └── OAuth2ClientCredentialsOperation.java │ │ └── dto │ │ ├── request │ │ ├── OAuth2ClientCredentialsRequest.java │ │ └── TokenRequest.java │ │ └── response │ │ └── TokenHolder.java │ └── test │ ├── java │ └── software │ │ └── reloadly │ │ └── sdk │ │ └── authentication │ │ ├── AuthenticationAPIMockServer.java │ │ ├── client │ │ └── AuthenticationAPITest.java │ │ ├── operation │ │ └── OAuth2ClientCredentialOperationsTest.java │ │ └── util │ │ └── RecordedRequestMatcher.java │ └── resources │ └── client │ ├── auth │ ├── error_access_denied.json │ ├── error_invalid_audience.json │ └── success_token_response.json │ └── error_invalid_token.json ├── java-sdk-core ├── .gitignore ├── pom.xml └── src │ └── main │ └── java │ └── software │ └── reloadly │ └── sdk │ └── core │ ├── constant │ └── ServiceURLs.java │ ├── dto │ ├── APIError.java │ └── response │ │ └── Page.java │ ├── enums │ ├── Environment.java │ └── Service.java │ ├── exception │ ├── APIException.java │ ├── RateLimitException.java │ ├── ReloadlyException.java │ └── oauth │ │ └── OAuthException.java │ ├── internal │ ├── adapter │ │ ├── JackSonDateDeserializer.java │ │ └── UTCDateDeserializer.java │ ├── client │ │ └── BaseOperation.java │ ├── constant │ │ ├── GrantType.java │ │ ├── HttpHeader.java │ │ └── MediaType.java │ ├── dto │ │ └── request │ │ │ ├── BaseRequest.java │ │ │ ├── CustomRequest.java │ │ │ ├── CustomizableRequest.java │ │ │ └── interfaces │ │ │ └── Request.java │ ├── enums │ │ └── Version.java │ ├── filter │ │ ├── BaseFilter.java │ │ └── QueryFilter.java │ ├── interceptor │ │ └── TelemetryInterceptor.java │ ├── net │ │ ├── API.java │ │ ├── ServiceAPI.java │ │ └── Telemetry.java │ └── util │ │ ├── Asserter.java │ │ ├── ExceptionUtil.java │ │ ├── RecordedRequestMatcher.java │ │ └── TelemetryUtil.java │ └── net │ ├── HttpOptions.java │ └── ProxyOptions.java ├── java-sdk-giftcard ├── .gitignore ├── USAGE.md ├── pom.xml ├── src │ ├── main │ │ └── java │ │ │ └── software │ │ │ └── reloadly │ │ │ └── sdk │ │ │ └── giftcard │ │ │ ├── client │ │ │ └── GiftcardAPI.java │ │ │ ├── dto │ │ │ ├── request │ │ │ │ └── GiftCardOrderRequest.java │ │ │ └── response │ │ │ │ ├── GiftcardBrand.java │ │ │ │ ├── GiftcardCountry.java │ │ │ │ ├── GiftcardDiscount.java │ │ │ │ ├── GiftcardInfo.java │ │ │ │ ├── GiftcardProduct.java │ │ │ │ ├── GiftcardRedeemInstruction.java │ │ │ │ ├── GiftcardRedeemInstructionSimplified.java │ │ │ │ └── GiftcardTransaction.java │ │ │ ├── enums │ │ │ ├── GiftCardTransactionStatus.java │ │ │ └── GiftcardDenominationType.java │ │ │ ├── filter │ │ │ ├── GiftcardProductFilter.java │ │ │ └── GiftcardTransactionFilter.java │ │ │ └── operation │ │ │ ├── BaseGiftcardOperation.java │ │ │ ├── GiftcardDiscountsOperations.java │ │ │ ├── GiftcardOrdersOperations.java │ │ │ ├── GiftcardProductOperations.java │ │ │ ├── GiftcardRedeemInstructionsOperations.java │ │ │ └── GiftcardTransactionsOperations.java │ └── test │ │ ├── java │ │ └── software │ │ │ └── reloadly │ │ │ └── sdk │ │ │ └── giftcard │ │ │ ├── client │ │ │ └── GiftcardAPITest.java │ │ │ ├── interfaces │ │ │ ├── IntegrationTest.java │ │ │ └── IntegrationTestWithProxy.java │ │ │ ├── operation │ │ │ ├── integration │ │ │ │ ├── BaseIntegrationTest.java │ │ │ │ ├── GiftcardDiscountsOperationsTests.java │ │ │ │ ├── GiftcardOrdersOperationsTests.java │ │ │ │ ├── GiftcardProductOperationsTests.java │ │ │ │ ├── GiftcardRedeemInstructionsOperationsTests.java │ │ │ │ └── GiftcardTransactionsOperationsTests.java │ │ │ └── unit │ │ │ │ ├── GiftcardDiscountsOperationsTests.java │ │ │ │ ├── GiftcardOrdersOperationsTests.java │ │ │ │ ├── GiftcardProductOperationsTests.java │ │ │ │ ├── GiftcardRedeemInstructionsOperationsTests.java │ │ │ │ └── GiftcardTransactionsOperationsTests.java │ │ │ └── util │ │ │ └── GiftcardAPIMockServer.java │ │ └── resources │ │ ├── discount │ │ ├── discount_response.json │ │ └── discounts_paged_unfiltered_response.json │ │ ├── order │ │ ├── transaction_redeem_response.json │ │ ├── transaction_with_recipient_email_response.json │ │ └── transaction_with_recipient_phone_response.json │ │ ├── product │ │ ├── product_by_id_response.json │ │ ├── products_list_by_country_filtered_response.json │ │ ├── products_list_by_country_unfiltered_response.json │ │ ├── products_paged_filtered_response.json │ │ └── products_paged_unfiltered_response.json │ │ ├── redeem_instructions │ │ ├── redeem_instructions_by_brand_response.json │ │ └── redeem_instructions_list_response.json │ │ └── transaction │ │ ├── transaction_by_id_response.json │ │ ├── transactions_paged_filtered_response.json │ │ └── transactions_paged_unfiltered_response.json └── usage │ ├── GIFTCARD-DISCOUNT-OPERATIONS.md │ ├── GIFTCARD-ORDER-OPERATIONS.md │ ├── GIFTCARD-PRODUCTS-OPERATIONS.md │ ├── GIFTCARD-REDEEM-INSTRUCTIONS-OPERATIONS.md │ └── GIFTCARD-TRANSACTIONS-OPERATIONS.md ├── lombok.config ├── mvnw └── pom.xml /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: openjdk:8-jdk 6 | steps: 7 | - checkout 8 | # Download and cache dependencies 9 | - restore_cache: 10 | keys: 11 | - v1-dependencies-{{ checksum "pom.xml" }} 12 | # fallback to using the latest cache if no exact match is found 13 | - v1-dependencies- 14 | # run tests! 15 | - run: ./mvnw clean verify 16 | - run: 17 | name: Upload Coverage 18 | when: on_success 19 | command: bash <(curl -s https://codecov.io/bash) -Z -C $CIRCLE_SHA1 20 | - save_cache: 21 | paths: 22 | - ~/.m2 23 | key: v1-dependencies-{{ checksum "pom.xml" }} 24 | environment: 25 | _JAVA_OPTIONS: "-Xms512m -Xmx1024m" 26 | TERM: dumb -------------------------------------------------------------------------------- /.codecov.yml: -------------------------------------------------------------------------------- 1 | coverage: 2 | precision: 2 3 | round: down 4 | range: "70...100" 5 | status: 6 | patch: 7 | default: 8 | if_no_uploads: error 9 | changes: true 10 | project: 11 | default: 12 | target: auto 13 | if_no_uploads: error 14 | comment: false -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F41B Bug report" 3 | about: Create a report to help us improve 4 | labels: bug, needs-triage 5 | --- 6 | 7 | 8 | 9 | ## Describe the bug 10 | 11 | 12 | ## Expected Behavior 13 | 14 | 15 | ## Current Behavior 16 | 17 | 18 | 19 | 20 | 21 | 22 | ## Steps to Reproduce 23 | 24 | 25 | 26 | 27 | ## Possible Solution 28 | 29 | 30 | ## Context 31 | 32 | 33 | 34 | ## Your Environment 35 | 36 | * Reloadly Java SDK version used: 37 | * JDK version used: 38 | * Operating System and version: 39 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/documentation.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4D5 Documentation Issue" 3 | about: Report an issue in the API Reference documentation or Developer Guide 4 | labels: documentation, needs-triage 5 | --- 6 | 7 | 8 | 9 | ## Describe the issue 10 | 11 | 12 | ## Links 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature-request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F680 Feature Request" 3 | about: Suggest an idea for this project 4 | labels: feature-request, needs-triage 5 | --- 6 | 7 | 8 | 9 | ## Describe the Feature 10 | 11 | 12 | ## Is your Feature Request related to a problem? 13 | 14 | 15 | ## Proposed Solution 16 | 17 | 18 | ## Describe alternatives you've considered 19 | 20 | 21 | ## Additional Context 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | - [ ] I may be able to implement this feature request 30 | 31 | 32 | ## Your Environment 33 | 34 | * Reloadly Java SDK version used: 35 | * JDK version used: 36 | * Operating System and version: 37 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/general-issue.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: "\U0001F4AC General Issue" 3 | about: Create a new issue 4 | labels: guidance, needs-triage 5 | --- 6 | 7 | 8 | 9 | ## Describe the issue 10 | 11 | 12 | ## Steps to Reproduce 13 | 14 | 15 | 16 | 17 | ## Current Behavior 18 | 19 | 20 | 21 | 22 | 23 | 24 | ## Your Environment 25 | 26 | * Reloadly Java SDK version used: 27 | * JDK version used: 28 | * Operating System and version: 29 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Changes 2 | 3 | Please describe both what is changing and why this is important. Include: 4 | 5 | - Endpoints added, deleted, deprecated, or changed 6 | - Classes and methods added, deleted, deprecated, or changed 7 | - Screenshots of new or changed UI, if applicable 8 | - A summary of usage if this is a new feature or change to a public API (this should also be added to relevant 9 | documentation once released) 10 | - Any alternative designs or approaches considered 11 | 12 | ### References 13 | 14 | Please include relevant links supporting this change such as a: 15 | 16 | - support ticket 17 | - community post 18 | - StackOverflow post 19 | - support forum thread 20 | 21 | ### Testing 22 | 23 | Please describe how this can be tested by reviewers. Be specific about anything not tested and reasons why. If this 24 | library has unit and/or integration testing, tests should be added for new functionality and existing tests should 25 | complete without errors. 26 | 27 | - [ ] This change adds test coverage 28 | - [ ] This change has been tested on the latest version of the platform/language or why not 29 | 30 | ### Checklist 31 | 32 | - [ ] I have read 33 | the [Reloadly general contribution guidelines](https://github.com/reloadly/reloadly-sdk-java/blob/master/GENERAL-CONTRIBUTING.md) 34 | - [ ] I have read 35 | the [Reloadly Code of Conduct](https://github.com/reloadly/reloadly-sdk-java/blob/master/CODE-OF-CONDUCT.md) 36 | - [ ] All existing and new tests complete without errors 37 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | # Configuration for probot-stale - https://github.com/probot/stale 2 | 3 | # Number of days of inactivity before an Issue or Pull Request becomes stale 4 | daysUntilStale: 90 5 | 6 | # Number of days of inactivity before an Issue or Pull Request with the stale label is closed. 7 | daysUntilClose: 7 8 | 9 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 10 | exemptLabels: [ ] 11 | 12 | # Set to true to ignore issues with an assignee (defaults to false) 13 | exemptAssignees: true 14 | 15 | # Label to use when marking as stale 16 | staleLabel: closed:stale 17 | 18 | # Comment to post when marking as stale. Set to `false` to disable 19 | markComment: > 20 | This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. If you have not received a response for our team (apologies for the delay) and this is still a blocker, please reply with additional information or just a ping. Thank you for your contribution! 🙇‍♂️ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | 26 | # OS generated files # 27 | ###################### 28 | .DS_Store 29 | .DS_Store? -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Reloadly/reloadly-sdk-java/bb0d890901ca41e2bb0fda79dbe91a358db85250/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2021 Reloadly, Inc. (https://www.reloadly.com) 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 NON-INFRINGEMENT. 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. -------------------------------------------------------------------------------- /SAMPLE-CODE.md: -------------------------------------------------------------------------------- 1 | # Sample Code 2 | 3 | ## Authentication SDK 4 | 5 | * [Usage](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-authentication/USAGE.md) 6 | 7 | ## Airtime SDK 8 | 9 | * [Overview](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/USAGE.md) (You should start here) 10 | * [Account Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/usage/ACCOUNT-OPERATIONS.md) 11 | * [Country Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/usage/COUNTRY-OPERATIONS.md) 12 | * [Discount Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/usage/DISCOUNT-OPERATIONS.md) 13 | * [Operator Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/usage/OPERATOR-OPERATIONS.md) 14 | * [Promotion Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/usage/PROMOTION-OPERATIONS.md) 15 | * [Report Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/usage/REPORT-OPERATIONS.md) 16 | * [Topup Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-airtime/usage/TOPUP-OPERATIONS.md) 17 | 18 | ## Giftcard SDK 19 | 20 | * [Overview](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-giftcard/USAGE.md) (You should start here) 21 | * [Giftcard Discount Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-giftcard/usage/GIFTCARD-DISCOUNT-OPERATIONS.md) 22 | * [Giftcard Order Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-giftcard/usage/GIFTCARD-ORDER-OPERATIONS.md) 23 | * [Giftcard Products Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-giftcard/usage/GIFTCARD-PRODUCTS-OPERATIONS.md) 24 | * [Giftcard Redeem Instructions Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-giftcard/usage/GIFTCARD-REDEEM-INSTRUCTIONS-OPERATIONS.md) 25 | * [Giftcard Transactions Operations](https://github.com/Reloadly/reloadly-sdk-java/blob/main/java-sdk-giftcard/usage/GIFTCARD-TRANSACTIONS-OPERATIONS.md) 26 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Reloadly/reloadly-sdk-java/bb0d890901ca41e2bb0fda79dbe91a358db85250/icon.png -------------------------------------------------------------------------------- /java-sdk-airtime/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | 26 | # OS generated files # 27 | ###################### 28 | .DS_Store 29 | .DS_Store? -------------------------------------------------------------------------------- /java-sdk-airtime/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | software.reloadly 8 | java-sdk 9 | 1.0.3 10 | 11 | 12 | 4.0.0 13 | java-sdk-airtime 14 | 1.0.3 15 | Reloadly Java SDK :: Services :: Reloadly Airtime Service 16 | https://github.com/reloadly/reloadly-sdk-java 17 | 18 | The Reloadly Java SDK for the Airtime API. The module holds the client classes 19 | that are used for communicating with Reloadly Airtime Service. 20 | 21 | 22 | 23 | 1.29 24 | 25 | 26 | 27 | 28 | software.reloadly 29 | java-sdk-authentication 30 | false 31 | 32 | 33 | com.neovisionaries 34 | nv-i18n 35 | ${nv-i18n.version} 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.jacoco 43 | jacoco-maven-plugin 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/Phone.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.neovisionaries.i18n.CountryCode; 6 | import lombok.*; 7 | 8 | import java.io.Serializable; 9 | 10 | @Getter 11 | @ToString 12 | @EqualsAndHashCode 13 | @RequiredArgsConstructor 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class Phone implements Serializable { 17 | 18 | private static final long serialVersionUID = -4426743606174460330L; 19 | 20 | /** 21 | * Phone number 22 | */ 23 | private final String number; 24 | 25 | /** 26 | * ISO 3166-1 alpha-2 Country code. See https://www.iso.org/obp/ui/#search 27 | */ 28 | private final CountryCode countryCode; 29 | } 30 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/request/EmailTopupRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import software.reloadly.sdk.airtime.dto.Phone; 5 | import lombok.Builder; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | 10 | import java.io.Serializable; 11 | 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode(callSuper = true) 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class EmailTopupRequest extends TopupRequest implements Serializable { 17 | 18 | private static final long serialVersionUID = 5538891031402350675L; 19 | 20 | /** 21 | * Destination recipient email address to be credited 22 | */ 23 | private final String recipientEmail; 24 | 25 | @Builder 26 | @SuppressWarnings("unused") 27 | public EmailTopupRequest(Double amount, Long operatorId, String recipientEmail, 28 | Phone senderPhone, boolean useLocalAmount, String customIdentifier) { 29 | 30 | super(amount, operatorId, senderPhone, useLocalAmount, customIdentifier); 31 | this.recipientEmail = recipientEmail; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/request/PhoneTopupRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import software.reloadly.sdk.airtime.dto.Phone; 5 | import lombok.Builder; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | 10 | import java.io.Serializable; 11 | 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode(callSuper = true) 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class PhoneTopupRequest extends TopupRequest implements Serializable { 17 | 18 | private static final long serialVersionUID = 588674246514588914L; 19 | 20 | /** 21 | * Destination recipient phone number (with country prefix) to be credited, example +50936377111 22 | */ 23 | private final Phone recipientPhone; 24 | 25 | @Builder 26 | @SuppressWarnings("unused") 27 | public PhoneTopupRequest(Double amount, Long operatorId, 28 | Phone recipientPhone, boolean useLocalAmount, Phone senderPhone, String customIdentifier) { 29 | 30 | super(amount, operatorId, senderPhone, useLocalAmount, customIdentifier); 31 | this.recipientPhone = recipientPhone; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/request/TopupRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.request; 2 | 3 | import software.reloadly.sdk.airtime.dto.Phone; 4 | import lombok.AllArgsConstructor; 5 | import lombok.Getter; 6 | 7 | import java.io.Serializable; 8 | 9 | @Getter 10 | @AllArgsConstructor 11 | public abstract class TopupRequest implements Topupable, Serializable { 12 | 13 | private static final long serialVersionUID = 4862758313864789228L; 14 | 15 | /** 16 | * Amount (in sender's currency) to credit recipient phone for 17 | */ 18 | private final Double amount; 19 | 20 | /** 21 | * Unique identifier of the destination mobile operator id 22 | */ 23 | private final Long operatorId; 24 | 25 | /** 26 | * Phone number of user requesting to credit the recipient phone, this field is optional. 27 | */ 28 | private final Phone senderPhone; 29 | 30 | /** 31 | * Indicates whether topup amount is a local amount (as in the same currency as the destination country) 32 | */ 33 | private final boolean useLocalAmount; 34 | 35 | /** 36 | * This field can be used to record any kind of info when performing the transaction. 37 | * Maximum length allowed for field customIdentifier is 150 characters, this field is optional. 38 | */ 39 | private final String customIdentifier; 40 | } 41 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/request/Topupable.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.request; 2 | 3 | import software.reloadly.sdk.airtime.dto.Phone; 4 | 5 | @SuppressWarnings("unused") 6 | public interface Topupable { 7 | Double getAmount(); 8 | Long getOperatorId(); 9 | Phone getSenderPhone(); 10 | boolean isUseLocalAmount(); 11 | } 12 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/AccountBalanceInfo.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | import software.reloadly.sdk.core.internal.adapter.JackSonDateDeserializer; 8 | import lombok.*; 9 | 10 | import java.io.Serializable; 11 | import java.math.BigDecimal; 12 | import java.util.Date; 13 | 14 | @Getter 15 | @ToString 16 | @EqualsAndHashCode 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | public class AccountBalanceInfo implements Serializable { 20 | 21 | private static final long serialVersionUID = 3027699901605787441L; 22 | 23 | /** 24 | * Current account balance amount 25 | */ 26 | @JsonProperty("balance") 27 | private BigDecimal amount; 28 | 29 | /** 30 | * Account ISO-4217 3 letter currency code. See https://www.iso.org/iso-4217-currency-codes.html. 31 | * Example : USD 32 | */ 33 | private String currencyCode; 34 | 35 | /** 36 | * Account currency name for the given currency code, example "United States Dollar" 37 | */ 38 | private String currencyName; 39 | 40 | /** 41 | * Account balance last updated date 42 | */ 43 | @JsonDeserialize(using = JackSonDateDeserializer.class) 44 | private Date updatedAt; 45 | } 46 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/AirtimeTransactionStatusResponse.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | import software.reloadly.sdk.airtime.enums.AirtimeTransactionStatus; 10 | 11 | import java.io.Serializable; 12 | 13 | @Getter 14 | @ToString 15 | @EqualsAndHashCode 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | public class AirtimeTransactionStatusResponse implements Serializable { 19 | 20 | private static final long serialVersionUID = -107316033333805883L; 21 | @JsonProperty("code") 22 | private String errorCode; 23 | @JsonProperty("message") 24 | private String errorMessage; 25 | private TopupTransaction transaction; 26 | private AirtimeTransactionStatus status; 27 | } 28 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/AsyncAirtimeResponse.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.io.Serializable; 10 | 11 | @Getter 12 | @ToString 13 | @EqualsAndHashCode 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class AsyncAirtimeResponse implements Serializable { 17 | 18 | private static final long serialVersionUID = -6885242699797871608L; 19 | private Long transactionId; 20 | } 21 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/Country.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.io.Serializable; 10 | import java.util.Set; 11 | 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class Country implements Serializable { 18 | 19 | private static final long serialVersionUID = -3646879608669281411L; 20 | 21 | /** 22 | * ISO 3166-1 alpha-2 Country code. See https://www.iso.org/obp/ui/#search 23 | */ 24 | private String isoName; 25 | 26 | /** 27 | * Full country name 28 | */ 29 | private String name; 30 | 31 | /** 32 | * Account ISO-4217 3 letter currency code for the given country. 33 | * See https://www.iso.org/iso-4217-currency-codes.html 34 | */ 35 | private String currencyCode; 36 | 37 | /** 38 | * Full currency name 39 | */ 40 | private String currencyName; 41 | 42 | /** 43 | * Symbol of currency 44 | */ 45 | private String currencySymbol; 46 | 47 | /** 48 | * Url of country flag image 49 | */ 50 | private String flag; 51 | 52 | /** 53 | * Calling codes of the country 54 | */ 55 | private Set callingCodes; 56 | } 57 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/Discount.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import software.reloadly.sdk.core.internal.adapter.JackSonDateDeserializer; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | 10 | import java.io.Serializable; 11 | import java.util.Date; 12 | 13 | @Getter 14 | @EqualsAndHashCode 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class Discount implements Serializable { 18 | 19 | private static final long serialVersionUID = 5891709864552029712L; 20 | private double percentage; 21 | private double internationalPercentage; 22 | private double localPercentage; 23 | @JsonDeserialize(using = JackSonDateDeserializer.class) 24 | private Date updatedAt; 25 | private SimplifiedOperator operator; 26 | } 27 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/FxRate.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.*; 6 | 7 | import java.io.Serializable; 8 | 9 | @Getter 10 | @ToString 11 | @EqualsAndHashCode 12 | @SuppressWarnings("unused") 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | public class FxRate implements Serializable { 16 | 17 | private static final long serialVersionUID = 1687686230114960005L; 18 | private float rate; 19 | private String currencyCode; 20 | } 21 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/GeographicalRechargePlan.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.*; 6 | 7 | import java.io.Serializable; 8 | import java.math.BigDecimal; 9 | import java.util.TreeMap; 10 | import java.util.TreeSet; 11 | 12 | @Getter 13 | @Setter 14 | @ToString 15 | @NoArgsConstructor 16 | @AllArgsConstructor 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | public class GeographicalRechargePlan implements Serializable { 20 | 21 | private static final long serialVersionUID = -5652377401301285195L; 22 | private String locationCode; 23 | private String locationName; 24 | private TreeSet fixedAmounts; 25 | private TreeSet localAmounts; 26 | private TreeMap fixedAmountsPlanNames; 27 | private TreeMap fixedAmountsDescriptions; 28 | private TreeMap localFixedAmountsPlanNames; 29 | private TreeMap localFixedAmountsDescriptions; 30 | } 31 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/OperatorFxRate.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | 9 | import java.io.Serializable; 10 | 11 | @Getter 12 | @EqualsAndHashCode 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | public class OperatorFxRate implements Serializable { 16 | 17 | private static final long serialVersionUID = 4787380284997559055L; 18 | @JsonProperty("id") 19 | private Long operatorId; 20 | @JsonProperty("name") 21 | private String operatorName; 22 | private float fxRate; 23 | private String currencyCode; 24 | } 25 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/PinDetail.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | 10 | import java.io.Serializable; 11 | 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class PinDetail implements Serializable { 18 | 19 | private static final long serialVersionUID = 4763811416861692554L; 20 | 21 | /** 22 | * Serial number 23 | */ 24 | private String serial; 25 | 26 | /** 27 | * Info part 1 28 | */ 29 | @JsonProperty("info1") 30 | private String info; 31 | 32 | /** 33 | * Info part 2 34 | */ 35 | @JsonProperty("info2") 36 | private String infoPart2; 37 | 38 | /** 39 | * Info part 3 40 | */ 41 | @JsonProperty("info3") 42 | private String infoPart3; 43 | 44 | /** 45 | * PIN value 46 | */ 47 | private Double value; 48 | 49 | /** 50 | * PIN code 51 | */ 52 | private String code; 53 | 54 | /** 55 | * PIN IVR info 56 | */ 57 | private String ivr; 58 | 59 | /** 60 | * PIN validity info 61 | */ 62 | private String validity; 63 | } 64 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/Promotion.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | import software.reloadly.sdk.core.internal.adapter.UTCDateDeserializer; 10 | 11 | import java.io.Serializable; 12 | import java.util.Date; 13 | 14 | @Getter 15 | @ToString 16 | @EqualsAndHashCode 17 | @JsonInclude(JsonInclude.Include.NON_NULL) 18 | @JsonIgnoreProperties(ignoreUnknown = true) 19 | public class Promotion implements Serializable { 20 | 21 | private static final long serialVersionUID = 81835466585326849L; 22 | 23 | /** 24 | * Unique identifier for the given promotion 25 | */ 26 | private Long id; 27 | 28 | /** 29 | * ID of operator to which the promotion applies 30 | */ 31 | private Long operatorId; 32 | 33 | /** 34 | * Title of the promotion 35 | */ 36 | private String title; 37 | 38 | /** 39 | * 2nd title for the promotion if any 40 | */ 41 | private String title2; 42 | 43 | /** 44 | * Description of the promotion 45 | */ 46 | private String description; 47 | 48 | /** 49 | * Date on which the promotion starts 50 | */ 51 | @JsonDeserialize(using = UTCDateDeserializer.class) 52 | private Date startDate; 53 | 54 | /** 55 | * Date on which the promotion ends 56 | */ 57 | @JsonDeserialize(using = UTCDateDeserializer.class) 58 | private Date endDate; 59 | 60 | /** 61 | * Amounts for which the promotion applies 62 | */ 63 | private String denominations; 64 | 65 | /** 66 | * Amounts (in destination country currency) for which the promotion applies 67 | */ 68 | private String localDenominations; 69 | } 70 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/SimplifiedCountry.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.io.Serializable; 10 | 11 | 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode 15 | @SuppressWarnings("unused") 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | @JsonIgnoreProperties(ignoreUnknown = true) 18 | public class SimplifiedCountry implements Serializable { 19 | 20 | private static final long serialVersionUID = -8942563311871329851L; 21 | 22 | /** 23 | * ISO 3166-1 alpha-2 Country code. See https://www.iso.org/obp/ui/#search 24 | */ 25 | private String isoName; 26 | 27 | /** 28 | * Full country name 29 | */ 30 | private String name; 31 | } 32 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/SimplifiedOperator.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | 8 | import java.io.Serializable; 9 | 10 | @Getter 11 | @EqualsAndHashCode 12 | @SuppressWarnings("unused") 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | public class SimplifiedOperator implements Serializable { 16 | 17 | private static final long serialVersionUID = -5051583749562465355L; 18 | private Long id; 19 | private String name; 20 | private String countryCode; 21 | private boolean data; 22 | private boolean bundle; 23 | } 24 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/dto/response/TransactionBalanceInfo.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | import software.reloadly.sdk.core.internal.adapter.JackSonDateDeserializer; 8 | import lombok.EqualsAndHashCode; 9 | import lombok.Getter; 10 | import lombok.ToString; 11 | 12 | import java.io.Serializable; 13 | import java.math.BigDecimal; 14 | import java.util.Date; 15 | 16 | @Getter 17 | @ToString 18 | @EqualsAndHashCode 19 | @JsonInclude(JsonInclude.Include.NON_NULL) 20 | @JsonIgnoreProperties(ignoreUnknown = true) 21 | public class TransactionBalanceInfo implements Serializable { 22 | 23 | private static final long serialVersionUID = 2189899214743926170L; 24 | 25 | /** 26 | * Account balance prior to the transaction 27 | */ 28 | @JsonProperty("oldBalance") 29 | private BigDecimal previousBalance; 30 | 31 | /** 32 | * Current account balance amount 33 | */ 34 | @JsonProperty("newBalance") 35 | private BigDecimal currentBalance; 36 | 37 | /** 38 | * Account ISO-4217 3 letter currency code. See https://www.iso.org/iso-4217-currency-codes.html. 39 | * Example : USD 40 | */ 41 | private String currencyCode; 42 | 43 | /** 44 | * Account currency name for the given currency code, example "United States Dollar" 45 | */ 46 | private String currencyName; 47 | 48 | /** 49 | * Account balance last updated date 50 | */ 51 | @JsonDeserialize(using = JackSonDateDeserializer.class) 52 | private Date updatedAt; 53 | } 54 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/enums/AirtimeTransactionStatus.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.enums; 2 | 3 | import java.io.Serializable; 4 | 5 | @SuppressWarnings("unused") 6 | public enum AirtimeTransactionStatus implements Serializable { 7 | PROCESSING, SUCCESSFUL, REFUNDED, FAILED 8 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/enums/DenominationType.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.enums; 2 | 3 | /** 4 | * Airtime operators supported denomination types. 5 | */ 6 | public enum DenominationType { 7 | FIXED, 8 | RANGE 9 | } 10 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/filter/TransactionHistoryFilter.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.filter; 2 | 3 | 4 | import com.neovisionaries.i18n.CountryCode; 5 | import lombok.NoArgsConstructor; 6 | import software.reloadly.sdk.airtime.operation.TopupOperations; 7 | import software.reloadly.sdk.core.internal.filter.QueryFilter; 8 | import software.reloadly.sdk.core.internal.util.Asserter; 9 | 10 | import java.time.LocalDateTime; 11 | import java.time.format.DateTimeFormatter; 12 | 13 | /** 14 | * Class used to filter the results received when calling topup transaction history endpoint. 15 | * Related to the {@link TopupOperations}. 16 | */ 17 | @NoArgsConstructor 18 | @SuppressWarnings("unused") 19 | public class TransactionHistoryFilter extends QueryFilter { 20 | 21 | private static final String END_DATE = "endDate"; 22 | private static final String START_DATE = "startDate"; 23 | private static final String OPERATOR_ID = "operatorId"; 24 | private static final String COUNTRY_CODE = "countryCode"; 25 | private static final String OPERATOR_NAME = "operatorName"; 26 | private static final String CUSTOM_IDENTIFIER = "customIdentifier"; 27 | 28 | @Override 29 | public TransactionHistoryFilter withPage(int pageNumber, int pageSize) { 30 | super.withPage(pageNumber, pageSize); 31 | return this; 32 | } 33 | 34 | /** 35 | * @param operatorId - Operator id to filter by 36 | * @return - TransactionHistoryFilter 37 | */ 38 | public TransactionHistoryFilter operatorId(Long operatorId) { 39 | Asserter.assertNotNull(operatorId, "Operator id"); 40 | Asserter.assertGreaterThanZero(operatorId, "Operator id"); 41 | parameters.put(OPERATOR_ID, operatorId); 42 | return this; 43 | } 44 | 45 | /** 46 | * @param countryCode - Country code to filter by 47 | * @return - TransactionHistoryFilter 48 | */ 49 | public TransactionHistoryFilter countryCode(CountryCode countryCode) { 50 | Asserter.assertNotNull(countryCode, "Country code"); 51 | parameters.put(COUNTRY_CODE, countryCode.getAlpha2()); 52 | return this; 53 | } 54 | 55 | /** 56 | * @param operatorName - Operator name to filter by 57 | * @return - TransactionHistoryFilter 58 | */ 59 | public TransactionHistoryFilter operatorName(String operatorName) { 60 | Asserter.assertNotBlank(operatorName, "Operator name"); 61 | parameters.put(OPERATOR_NAME, operatorName); 62 | return this; 63 | } 64 | 65 | /** 66 | * @param customIdentifier - Custom identifier to filter by 67 | * @return - TransactionHistoryFilter 68 | */ 69 | public TransactionHistoryFilter customIdentifier(String customIdentifier) { 70 | Asserter.assertNotBlank(customIdentifier, "Custom identifier"); 71 | parameters.put(CUSTOM_IDENTIFIER, customIdentifier); 72 | return this; 73 | } 74 | 75 | /** 76 | * @param startDate - Date range start date to filter by 77 | * @return - TransactionHistoryFilter 78 | */ 79 | public TransactionHistoryFilter startDate(LocalDateTime startDate) { 80 | Asserter.assertNotNull(startDate, "Start date"); 81 | parameters.put(START_DATE, startDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 82 | return this; 83 | } 84 | 85 | /** 86 | * @param endDate - Date range end date to filter by 87 | * @return - TransactionHistoryFilter 88 | */ 89 | public TransactionHistoryFilter endDate(LocalDateTime endDate) { 90 | Asserter.assertNotNull(endDate, "End date"); 91 | parameters.put(END_DATE, endDate.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); 92 | return this; 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/internal/dto/request/FxRateRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.internal.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonInclude; 4 | import lombok.EqualsAndHashCode; 5 | import lombok.Getter; 6 | import lombok.RequiredArgsConstructor; 7 | import lombok.ToString; 8 | 9 | import java.io.Serializable; 10 | 11 | @Getter 12 | @ToString 13 | @EqualsAndHashCode 14 | @RequiredArgsConstructor 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | public class FxRateRequest implements Serializable { 17 | 18 | private static final long serialVersionUID = 6823503731663525315L; 19 | private final Double amount; 20 | } 21 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/operation/AccountOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import software.reloadly.sdk.airtime.dto.response.AccountBalanceInfo; 5 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 6 | import okhttp3.HttpUrl; 7 | import okhttp3.OkHttpClient; 8 | 9 | public class AccountOperations extends BaseAirtimeOperation { 10 | 11 | private static final String END_POINT = "accounts"; 12 | private static final String PATH_BALANCE = "balance"; 13 | 14 | public AccountOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 15 | super(baseUrl, apiToken, client); 16 | } 17 | 18 | public Request getBalance() { 19 | return createGetRequest(getBuilder(END_POINT).addPathSegments(PATH_BALANCE).build().toString(), 20 | new TypeReference() { 21 | } 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/operation/BaseAirtimeOperation.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import software.reloadly.sdk.core.internal.client.BaseOperation; 5 | import software.reloadly.sdk.core.internal.constant.HttpHeader; 6 | import software.reloadly.sdk.core.internal.constant.MediaType; 7 | import software.reloadly.sdk.core.internal.dto.request.CustomRequest; 8 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 9 | import software.reloadly.sdk.core.internal.enums.Version; 10 | import software.reloadly.sdk.core.internal.filter.QueryFilter; 11 | import okhttp3.HttpUrl; 12 | import okhttp3.OkHttpClient; 13 | 14 | abstract class BaseAirtimeOperation extends BaseOperation { 15 | 16 | BaseAirtimeOperation(HttpUrl baseUrl, String apiToken, OkHttpClient client) { 17 | super(baseUrl, apiToken, client); 18 | } 19 | 20 | HttpUrl.Builder buildFilters(QueryFilter filter, String endPoint) { 21 | HttpUrl.Builder builder = getBuilder(endPoint); 22 | 23 | if (filter != null) { 24 | filter.getParameters().forEach((key, value) -> builder.addQueryParameter(key, String.valueOf(value))); 25 | } 26 | 27 | return builder; 28 | } 29 | 30 | protected Request createGetRequest(String url, TypeReference type) { 31 | CustomRequest request = new CustomRequest<>(client, url, "GET", type); 32 | request.addHeader(HttpHeader.ACCEPT, Version.AIRTIME_V1.getValue()); 33 | request.addHeader(HttpHeader.AUTHORIZATION, "Bearer " + apiToken); 34 | return request; 35 | } 36 | 37 | protected Request createPostRequest(String url, Object body, TypeReference type) { 38 | return new CustomRequest<>(client, url, "POST", type) 39 | .addHeader(HttpHeader.ACCEPT, Version.AIRTIME_V1.getValue()) 40 | .addHeader(HttpHeader.CONTENT_TYPE, MediaType.APPLICATION_JSON) 41 | .addHeader(HttpHeader.AUTHORIZATION, "Bearer " + apiToken) 42 | .setBody(body); 43 | } 44 | 45 | protected HttpUrl.Builder getBuilder(String endPoint) { 46 | return baseUrl.newBuilder().addPathSegments(endPoint); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/operation/CountryOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.neovisionaries.i18n.CountryCode; 5 | import software.reloadly.sdk.airtime.dto.response.Country; 6 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 7 | import software.reloadly.sdk.core.internal.util.Asserter; 8 | import okhttp3.HttpUrl; 9 | import okhttp3.OkHttpClient; 10 | 11 | import java.util.List; 12 | 13 | public class CountryOperations extends BaseAirtimeOperation { 14 | 15 | private static final String END_POINT = "countries"; 16 | 17 | public CountryOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 18 | super(baseUrl, apiToken, client); 19 | } 20 | 21 | public Request> list() { 22 | return createGetRequest(getBuilder(END_POINT).build().toString(), new TypeReference>() { 23 | }); 24 | } 25 | 26 | public Request getByCode(CountryCode countryCode) { 27 | Asserter.assertNotNull(countryCode, "Country code"); 28 | return createGetRequest(getBuilder(END_POINT).addPathSegments(countryCode.getAlpha2()).build().toString(), 29 | new TypeReference() { 30 | } 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/operation/DiscountOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | import software.reloadly.sdk.airtime.dto.response.Discount; 7 | import software.reloadly.sdk.core.dto.response.Page; 8 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 9 | import software.reloadly.sdk.core.internal.filter.QueryFilter; 10 | import software.reloadly.sdk.core.internal.util.Asserter; 11 | 12 | public class DiscountOperations extends BaseAirtimeOperation { 13 | 14 | private static final String END_POINT = "operators"; 15 | private static final String PATH_SEGMENT_DISCOUNT = "commissions"; 16 | 17 | 18 | public DiscountOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 19 | super(baseUrl, apiToken, client); 20 | } 21 | 22 | public Request> list() { 23 | return createGetRequest(getBuilder(END_POINT).addPathSegments(PATH_SEGMENT_DISCOUNT).build().toString(), 24 | new TypeReference>() { 25 | } 26 | ); 27 | } 28 | 29 | public Request> list(QueryFilter filter) { 30 | return createGetRequest(buildFilters(filter, END_POINT) 31 | .addPathSegments(PATH_SEGMENT_DISCOUNT).build().toString(), 32 | new TypeReference>() { 33 | } 34 | ); 35 | } 36 | 37 | public Request getByOperatorId(Long operatorId) { 38 | Asserter.assertNotNull(operatorId, "Operator id"); 39 | Asserter.assertGreaterThanZero(operatorId, "Operator id"); 40 | return createGetRequest(getBuilder(END_POINT).addPathSegments(String.valueOf(operatorId)) 41 | .addPathSegments(PATH_SEGMENT_DISCOUNT).build().toString(), 42 | new TypeReference() { 43 | } 44 | ); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/operation/PromotionOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.neovisionaries.i18n.CountryCode; 5 | import software.reloadly.sdk.airtime.dto.response.Promotion; 6 | import software.reloadly.sdk.core.dto.response.Page; 7 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 8 | import software.reloadly.sdk.core.internal.filter.QueryFilter; 9 | import software.reloadly.sdk.core.internal.util.Asserter; 10 | import okhttp3.HttpUrl; 11 | import okhttp3.OkHttpClient; 12 | 13 | import java.util.List; 14 | 15 | public class PromotionOperations extends BaseAirtimeOperation { 16 | 17 | private static final String END_POINT = "promotions"; 18 | private static final String PATH_SEGMENT_COUNTRIES = "countries"; 19 | private static final String PATH_SEGMENT_OPERATORS = "operators"; 20 | 21 | public PromotionOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 22 | super(baseUrl, apiToken, client); 23 | } 24 | 25 | public Request> list() { 26 | return createGetRequest(getBuilder(END_POINT).build().toString(), new TypeReference>() { 27 | }); 28 | } 29 | 30 | public Request> list(QueryFilter filter) { 31 | return createGetRequest(buildFilters(filter, END_POINT).build().toString(), 32 | new TypeReference>() { 33 | } 34 | ); 35 | } 36 | 37 | public Request getById(Long promotionId) { 38 | Asserter.assertNotNull(promotionId, "Promotion id"); 39 | Asserter.assertGreaterThanZero(promotionId, "Promotion id"); 40 | return createGetRequest(getBuilder(END_POINT).addPathSegments(String.valueOf(promotionId)).build().toString(), 41 | new TypeReference() { 42 | } 43 | ); 44 | } 45 | 46 | public Request> getByCountryCode(CountryCode countryCode) { 47 | Asserter.assertNotNull(countryCode, "Country code"); 48 | return createGetRequest(getBuilder(END_POINT).addPathSegments(PATH_SEGMENT_COUNTRIES) 49 | .addPathSegments(countryCode.getAlpha2()).build().toString(), 50 | new TypeReference>() { 51 | } 52 | ); 53 | } 54 | 55 | public Request> getByOperatorId(Long operatorId) { 56 | Asserter.assertNotNull(operatorId, "Operator id"); 57 | Asserter.assertGreaterThanZero(operatorId, "Operator id"); 58 | return createGetRequest(getBuilder(END_POINT).addPathSegments(PATH_SEGMENT_OPERATORS) 59 | .addPathSegments(String.valueOf(operatorId)).build().toString(), 60 | new TypeReference>() { 61 | } 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/operation/ReportOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation; 2 | 3 | import okhttp3.HttpUrl; 4 | import okhttp3.OkHttpClient; 5 | 6 | public class ReportOperations extends BaseAirtimeOperation { 7 | 8 | public ReportOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 9 | super(baseUrl, apiToken, client); 10 | } 11 | 12 | public TransactionHistoryOperations transactionsHistory() { 13 | return new TransactionHistoryOperations(client, baseUrl, apiToken); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/main/java/software/reloadly/sdk/airtime/operation/TransactionHistoryOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | import org.apache.commons.lang3.StringUtils; 7 | import software.reloadly.sdk.airtime.dto.response.TopupTransaction; 8 | import software.reloadly.sdk.airtime.filter.TransactionHistoryFilter; 9 | import software.reloadly.sdk.core.dto.response.Page; 10 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 11 | import software.reloadly.sdk.core.internal.util.Asserter; 12 | 13 | import java.time.LocalDateTime; 14 | import java.time.format.DateTimeFormatter; 15 | 16 | public class TransactionHistoryOperations extends BaseAirtimeOperation { 17 | 18 | private static final String TOPUP_TRANSACTION_HISTORY_END_POINT = "topups/reports/transactions"; 19 | 20 | public TransactionHistoryOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 21 | super(baseUrl, apiToken, client); 22 | } 23 | 24 | public Request> list() { 25 | return createGetRequest(getBuilder(TOPUP_TRANSACTION_HISTORY_END_POINT).build().toString(), 26 | new TypeReference>() { 27 | } 28 | ); 29 | } 30 | 31 | public Request> list(TransactionHistoryFilter filter) { 32 | validateStartAndEndDate(filter); 33 | return createGetRequest(buildFilters(filter, TOPUP_TRANSACTION_HISTORY_END_POINT).build().toString(), 34 | new TypeReference>() { 35 | } 36 | ); 37 | } 38 | 39 | public Request getById(Long transactionId) { 40 | Asserter.assertNotNull(transactionId, "Transaction id"); 41 | Asserter.assertGreaterThanZero(transactionId, "Transaction id"); 42 | return createGetRequest(getBuilder(TOPUP_TRANSACTION_HISTORY_END_POINT) 43 | .addPathSegments(String.valueOf(transactionId)).build().toString(), 44 | new TypeReference() { 45 | } 46 | ); 47 | } 48 | 49 | private void validateStartAndEndDate(TransactionHistoryFilter filter) { 50 | 51 | LocalDateTime endDate = null; 52 | LocalDateTime startDate = null; 53 | final String END_DATE = "endDate"; 54 | final String START_DATE = "startDate"; 55 | DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); 56 | String endDateStr = (String) filter.getParameters().getOrDefault(END_DATE, null); 57 | String startDateStr = (String) filter.getParameters().getOrDefault(START_DATE, null); 58 | 59 | if (StringUtils.isNotBlank(endDateStr)) { 60 | endDate = LocalDateTime.parse(endDateStr, dateFormatter); 61 | } 62 | 63 | if (StringUtils.isNotBlank(startDateStr)) { 64 | startDate = LocalDateTime.parse(startDateStr, dateFormatter); 65 | } 66 | 67 | if ((startDate == null && endDate != null) || (startDate != null && endDate == null)) { 68 | String msg = "If start date is set, end date must be set as well and vice-versa"; 69 | throw new IllegalArgumentException(msg); 70 | } else if (startDate != null && startDate.isAfter(endDate)) { 71 | String msg = "The start date must NOT be greater than the end date"; 72 | throw new IllegalArgumentException(msg); 73 | } else if (endDate != null && endDate.isBefore(startDate)) { 74 | String msg = "The end date must be greater than the start date"; 75 | throw new IllegalArgumentException(msg); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/AsserterTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | import software.reloadly.sdk.core.internal.util.Asserter; 6 | 7 | import java.util.ArrayList; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertThrows; 10 | 11 | public class AsserterTest { 12 | 13 | @Test 14 | public void testAssertValidUrlThrowsAnExceptionWhenUrlIsNotValid() { 15 | Throwable exception = assertThrows(IllegalArgumentException.class, () -> 16 | Asserter.assertValidUrl("some-invalid-url", "Url")); 17 | Assertions.assertEquals("'Url' must be a valid URL!", exception.getMessage()); 18 | } 19 | 20 | @Test 21 | public void testAssertNotEmptyThrowsAnExceptionWhenUrlCollectionIsNull() { 22 | Throwable exception = assertThrows(IllegalArgumentException.class, () -> 23 | Asserter.assertNotEmpty(null, "Urls")); 24 | Assertions.assertEquals("'Urls' cannot be null!", exception.getMessage()); 25 | } 26 | 27 | @Test 28 | public void testAssertNotEmptyThrowsAnExceptionWhenUrlCollectionIsEmpty() { 29 | Throwable exception = assertThrows(IllegalArgumentException.class, () -> 30 | Asserter.assertNotEmpty(new ArrayList<>(), "Urls")); 31 | Assertions.assertEquals("'Urls' cannot be empty!", exception.getMessage()); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/ExceptionUtilTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime; 2 | 3 | import org.junit.jupiter.api.Assertions; 4 | import org.junit.jupiter.api.Test; 5 | import software.reloadly.sdk.core.dto.APIError; 6 | import software.reloadly.sdk.core.exception.APIException; 7 | import software.reloadly.sdk.core.exception.oauth.OAuthException; 8 | import software.reloadly.sdk.core.internal.util.ExceptionUtil; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Date; 12 | 13 | public class ExceptionUtilTest { 14 | 15 | @Test 16 | public void testConvert1_Happy_Path() { 17 | String message = "Some error has occurred"; 18 | String path = "/some-api-end-point"; 19 | Date timeStamp = new Date(); 20 | APIError apiError = new APIError(message, path, timeStamp); 21 | APIException apiException = ExceptionUtil.convert(apiError, 500); 22 | Assertions.assertNotNull(apiException); 23 | } 24 | 25 | @Test 26 | public void testConvert2_Happy_Path() { 27 | String message = "Some error has occurred"; 28 | String path = "/some-api-end-point"; 29 | Date timeStamp = new Date(); 30 | Throwable throwable = new Throwable(message); 31 | APIError apiError = new APIError(message, path, timeStamp); 32 | APIException apiException = ExceptionUtil.convert(apiError, 500, throwable); 33 | Assertions.assertNotNull(apiException); 34 | } 35 | 36 | @Test 37 | public void testConvertReturnsOAuthExceptionType() { 38 | String message = "Some error has occurred"; 39 | String path = "/oauth/token"; 40 | Date timeStamp = new Date(); 41 | Throwable throwable = new Throwable(message); 42 | APIError apiError = new APIError(message, path, timeStamp); 43 | APIException apiException = ExceptionUtil.convert(apiError, 500, throwable); 44 | Assertions.assertTrue(apiException instanceof OAuthException); 45 | } 46 | 47 | @Test 48 | public void testConvertReturnsAdditionalFields() { 49 | String message = "Some error has occurred"; 50 | String path = "/oauth/token"; 51 | Date timeStamp = new Date(); 52 | Throwable throwable = new Throwable(message); 53 | APIError apiError = new APIError(message, path, timeStamp); 54 | apiError.setErrorCode("TOKEN_EXPIRED"); 55 | apiError.setDetails(new ArrayList<>()); 56 | APIException apiException = ExceptionUtil.convert(apiError, 500, throwable); 57 | Assertions.assertTrue(apiException instanceof OAuthException); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/client/integration/AirtimeAPITest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.client.integration; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import org.junit.jupiter.api.Test; 5 | import software.reloadly.sdk.airtime.client.AirtimeAPI; 6 | import software.reloadly.sdk.airtime.dto.response.Operator; 7 | import software.reloadly.sdk.airtime.util.ExpiredToken; 8 | import software.reloadly.sdk.core.enums.Environment; 9 | import software.reloadly.sdk.core.exception.ReloadlyException; 10 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.is; 14 | import static org.hamcrest.Matchers.notNullValue; 15 | 16 | public class AirtimeAPITest { 17 | 18 | private static String clientId; 19 | private static String clientSecret; 20 | 21 | @BeforeAll 22 | static void beforeAll() { 23 | clientId = System.getenv("LIVE_CLIENT_ID"); 24 | clientSecret = System.getenv("LIVE_CLIENT_SECRET"); 25 | } 26 | 27 | @Test 28 | public void testObtainingNewTokenAutomaticallyWhenSpecifiedTokenIsExpiredOrInvalid() throws ReloadlyException { 29 | Long operatorId = 174L; 30 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 31 | .clientId(clientId) 32 | .clientSecret(clientSecret) 33 | .environment(Environment.LIVE) 34 | .accessToken(ExpiredToken.AIRTIME) 35 | .build(); 36 | 37 | Request request = airtimeAPI.operators().getById(operatorId); 38 | Operator operator = request.execute(); 39 | assertThat(operator, is(notNullValue())); 40 | } 41 | 42 | @Test 43 | public void testRefreshToken() throws ReloadlyException { 44 | 45 | Long operatorId = 174L; 46 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 47 | .clientId(clientId) 48 | .clientSecret(clientSecret) 49 | .environment(Environment.LIVE) 50 | .accessToken(ExpiredToken.AIRTIME) 51 | .build(); 52 | 53 | Request request = airtimeAPI.operators().getById(operatorId); 54 | airtimeAPI.refreshAccessToken(request); 55 | Operator operator = request.execute(); 56 | assertThat(operator, is(notNullValue())); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/interfaces/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.interfaces; 2 | 3 | import org.junit.jupiter.api.Tag; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Test 12 | @Tag("integration") 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface IntegrationTest { 16 | } 17 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/interfaces/IntegrationTestWithProxy.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.interfaces; 2 | 3 | import org.junit.jupiter.api.Tag; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Test 12 | @Tag("integration-with-proxy") 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface IntegrationTestWithProxy { 16 | } 17 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/operation/integration/AccountOperationsTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation.integration; 2 | 3 | import software.reloadly.sdk.airtime.client.AirtimeAPI; 4 | import software.reloadly.sdk.airtime.dto.response.AccountBalanceInfo; 5 | import software.reloadly.sdk.airtime.interfaces.IntegrationTest; 6 | import software.reloadly.sdk.core.enums.Environment; 7 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 8 | 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import static org.hamcrest.MatcherAssert.assertThat; 13 | import static org.hamcrest.Matchers.*; 14 | 15 | public class AccountOperationsTest extends BaseIntegrationTest { 16 | 17 | @IntegrationTest 18 | public void testGetAccountBalance() throws Exception { 19 | 20 | AirtimeAPI airtimeAPI = AirtimeAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 21 | Request request = airtimeAPI.accounts().getBalance(); 22 | assertThat(request, is(notNullValue())); 23 | AccountBalanceInfo accountBalance = request.execute(); 24 | 25 | assertIsValidAccountBalance(accountBalance); 26 | } 27 | 28 | private void assertIsValidAccountBalance(AccountBalanceInfo accountBalance) { 29 | 30 | List countryFields = Arrays.asList("amount", "currencyCode", "currencyName", "updatedAt"); 31 | countryFields.forEach(field -> assertThat(accountBalance, hasProperty(field))); 32 | assertThat(accountBalance.getAmount(), is(notNullValue())); 33 | assertThat(accountBalance.getCurrencyCode(), is(notNullValue())); 34 | assertThat(accountBalance.getCurrencyName(), is(notNullValue())); 35 | assertThat(accountBalance.getUpdatedAt(), is(notNullValue())); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/operation/integration/BaseIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation.integration; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import software.reloadly.sdk.authentication.client.AuthenticationAPI; 5 | import software.reloadly.sdk.core.enums.Service; 6 | import software.reloadly.sdk.core.exception.ReloadlyException; 7 | 8 | public class BaseIntegrationTest { 9 | 10 | protected static String accessToken; 11 | protected static String sandboxAccessToken; 12 | 13 | @BeforeAll 14 | static void beforeAll() throws ReloadlyException { 15 | String clientId = System.getenv("LIVE_CLIENT_ID"); 16 | String clientSecret = System.getenv("LIVE_CLIENT_SECRET"); 17 | String sandboxClientId = System.getenv("SANDBOX_CLIENT_ID"); 18 | String sandboxClientSecret = System.getenv("SANDBOX_CLIENT_SECRET"); 19 | 20 | accessToken = AuthenticationAPI.builder().clientId(clientId) 21 | .clientSecret(clientSecret) 22 | .service(Service.AIRTIME) 23 | .build().clientCredentials().getAccessToken().execute().getToken(); 24 | 25 | sandboxAccessToken = AuthenticationAPI.builder().clientId(sandboxClientId) 26 | .clientSecret(sandboxClientSecret) 27 | .service(Service.AIRTIME_SANDBOX) 28 | .build().clientCredentials().getAccessToken().execute().getToken(); 29 | } 30 | 31 | 32 | } 33 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/operation/integration/CountryOperationsTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation.integration; 2 | 3 | import com.neovisionaries.i18n.CountryCode; 4 | import software.reloadly.sdk.airtime.client.AirtimeAPI; 5 | import software.reloadly.sdk.airtime.dto.response.Country; 6 | import software.reloadly.sdk.airtime.interfaces.IntegrationTest; 7 | import software.reloadly.sdk.core.enums.Environment; 8 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 9 | 10 | import java.util.Arrays; 11 | import java.util.List; 12 | 13 | import static org.hamcrest.MatcherAssert.assertThat; 14 | import static org.hamcrest.Matchers.*; 15 | 16 | public class CountryOperationsTest extends BaseIntegrationTest { 17 | 18 | @IntegrationTest 19 | public void testListCountries() throws Exception { 20 | 21 | AirtimeAPI airtimeAPI = AirtimeAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 22 | Request> request = airtimeAPI.countries().list(); 23 | assertThat(request, is(notNullValue())); 24 | List countries = request.execute(); 25 | 26 | countries.forEach(this::assertIsValidCountry); 27 | } 28 | 29 | @IntegrationTest 30 | public void testGetByCountryCode() throws Exception { 31 | 32 | AirtimeAPI airtimeAPI = AirtimeAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 33 | Request request = airtimeAPI.countries().getByCode(CountryCode.HT); 34 | assertThat(request, is(notNullValue())); 35 | Country country = request.execute(); 36 | assertIsValidCountry(country); 37 | } 38 | 39 | private void assertIsValidCountry(Country country) { 40 | 41 | List countryFields = Arrays.asList("isoName", "name", "currencyCode", "currencyName", "currencySymbol", 42 | "flag", "callingCodes" 43 | ); 44 | 45 | countryFields.forEach(field -> assertThat(country, hasProperty(field))); 46 | 47 | assertThat(country.getIsoName(), is(notNullValue())); 48 | assertThat(country.getName(), is(notNullValue())); 49 | assertThat(country.getCurrencyCode(), is(notNullValue())); 50 | assertThat(country.getCurrencyName(), is(notNullValue())); 51 | assertThat(country.getCurrencySymbol(), is(notNullValue())); 52 | assertThat(country.getFlag(), is(notNullValue())); 53 | assertThat(country.getCallingCodes(), is(notNullValue())); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/operation/integration/DiscountOperationsTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.operation.integration; 2 | 3 | import software.reloadly.sdk.airtime.client.AirtimeAPI; 4 | import software.reloadly.sdk.airtime.dto.response.Discount; 5 | import software.reloadly.sdk.airtime.interfaces.IntegrationTest; 6 | import software.reloadly.sdk.core.dto.response.Page; 7 | import software.reloadly.sdk.core.enums.Environment; 8 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 9 | import software.reloadly.sdk.core.internal.filter.QueryFilter; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.Matchers.*; 16 | 17 | public class DiscountOperationsTest extends BaseIntegrationTest { 18 | 19 | @IntegrationTest 20 | public void testListDiscounts() throws Exception { 21 | 22 | AirtimeAPI airtimeAPI =AirtimeAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 23 | Request> request = airtimeAPI.discounts().list(); 24 | assertThat(request, is(notNullValue())); 25 | Page discountPage = request.execute(); 26 | discountPage.getContent().forEach(this::assertIsValidDiscount); 27 | } 28 | 29 | @IntegrationTest 30 | public void testListDiscountsWithFilters() throws Exception { 31 | 32 | AirtimeAPI airtimeAPI =AirtimeAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 33 | QueryFilter filter = new QueryFilter().withPage(1, 200); 34 | Request> request = airtimeAPI.discounts().list(filter); 35 | assertThat(request, is(notNullValue())); 36 | Page discountPage = request.execute(); 37 | discountPage.getContent().forEach(this::assertIsValidDiscount); 38 | } 39 | 40 | @IntegrationTest 41 | public void testGetByOperatorId() throws Exception { 42 | 43 | Long operatorId = 174L; 44 | AirtimeAPI airtimeAPI =AirtimeAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 45 | Request request = airtimeAPI.discounts().getByOperatorId(operatorId); 46 | assertThat(request, is(notNullValue())); 47 | Discount discount = request.execute(); 48 | assertIsValidDiscount(discount); 49 | } 50 | 51 | private void assertIsValidDiscount(Discount discount) { 52 | 53 | List countryFields = Arrays.asList( 54 | "internationalPercentage", "localPercentage", "updatedAt", "operator" 55 | ); 56 | 57 | countryFields.forEach(field -> assertThat(discount, hasProperty(field))); 58 | 59 | assertThat(discount.getPercentage(), is(notNullValue())); 60 | assertThat(discount.getInternationalPercentage(), is(notNullValue())); 61 | assertThat(discount.getLocalPercentage(), is(notNullValue())); 62 | assertThat(discount.getUpdatedAt(), is(notNullValue())); 63 | assertThat(discount.getOperator(), is(notNullValue())); 64 | assertThat(discount.getOperator().getId(), is(notNullValue())); 65 | assertThat(discount.getOperator().getName(), is(notNullValue())); 66 | assertThat(discount.getOperator().getCountryCode(), is(notNullValue())); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/util/AirtimeAPIMockServer.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.util; 2 | 3 | import okhttp3.mockwebserver.MockResponse; 4 | import okhttp3.mockwebserver.MockWebServer; 5 | import okhttp3.mockwebserver.RecordedRequest; 6 | import software.reloadly.sdk.core.internal.constant.HttpHeader; 7 | import software.reloadly.sdk.core.internal.enums.Version; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Paths; 12 | 13 | public class AirtimeAPIMockServer { 14 | 15 | public static final String ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJJc3N1ZXIiOiJyZWxvYWRseSIsImV4cCI6MTY3NTY0N" + 16 | "DE5OTgsImlhdCI6MTYwOTEzOTU5OH0.fKuCwsOC7d6oDEG2hZdQSwrtwQtxQUx9ueRXlt_9mtA"; 17 | 18 | private final MockWebServer server; 19 | 20 | public AirtimeAPIMockServer() throws Exception { 21 | server = new MockWebServer(); 22 | server.start(); 23 | } 24 | 25 | public void stop() throws IOException { 26 | server.shutdown(); 27 | } 28 | 29 | public String getBaseUrl() { 30 | return server.url("").toString().replaceAll("/\\z", ""); 31 | } 32 | 33 | public RecordedRequest takeRequest() throws InterruptedException { 34 | return server.takeRequest(); 35 | } 36 | 37 | private String readTextFile(String path) throws IOException { 38 | return new String(Files.readAllBytes(Paths.get(path))); 39 | } 40 | 41 | public void jsonResponse(String path, int statusCode) throws IOException { 42 | MockResponse response = new MockResponse() 43 | .setResponseCode(statusCode) 44 | .addHeader(HttpHeader.CONTENT_TYPE, Version.AIRTIME_V1.getValue()) 45 | .setBody(readTextFile(path)); 46 | server.enqueue(response); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/java/software/reloadly/sdk/airtime/util/ExpiredToken.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.airtime.util; 2 | 3 | public class ExpiredToken { 4 | public static final String AIRTIME = "eyJraWQiOiI0YzU3YTMyMy1lZWNmLTRkM2QtODI5OC02ZjdmYTk5NjBlYzMiLCJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiI5OTcyIiwiaXNzIjoiaHR0cHM6Ly9yZWxvYWRseS1kZXYuYXV0aDAuY29tLyIsImh0dHBzOi8vcmVsb2FkbHkuY29tL3NhbmRib3giOmZhbHNlLCJodHRwczovL3JlbG9hZGx5LmNvbS9wcmVwYWlkVXNlcklkIjoiOTk3MiIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyIsImF1ZCI6Imh0dHBzOi8vdG9wdXBzLWhzMjU2LWRldi5yZWxvYWRseS5jb20iLCJuYmYiOjE2MzQxNjAyNDEsImF6cCI6Ijk5NzIiLCJzY29wZSI6InNlbmQtdG9wdXBzIHJlYWQtb3BlcmF0b3JzIHJlYWQtcHJvbW90aW9ucyByZWFkLXRvcHVwcy1oaXN0b3J5IHJlYWQtcHJlcGFpZC1iYWxhbmNlIHJlYWQtcHJlcGFpZC1jb21taXNzaW9ucyIsImV4cCI6MTYzOTM0Nzg0MSwiaHR0cHM6Ly9yZWxvYWRseS5jb20vanRpIjoiNWVhZWI0M2UtMmNjYy00NWUyLTg1N2ItYjNhYmMzNDg1M2FkIiwiaWF0IjoxNjM0MTYwMjQxLCJqdGkiOiIyNzgyN2E4Yy05OGFlLTRmN2EtOThkYi1kNTc3OGNiYzA4NmEifQ.FqATJFKgdySkojseu2xN2TVsTsg05ZHYrmUu_D5eZCE"; 5 | } 6 | -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/account/account_balance.json: -------------------------------------------------------------------------------- 1 | { 2 | "balance": 1000.00, 3 | "currencyCode": "USD", 4 | "currencyName": "US Dollar", 5 | "updatedAt": "2020-12-26 11:57:22" 6 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/country/country.json: -------------------------------------------------------------------------------- 1 | { 2 | "isoName": "US", 3 | "name": "United States", 4 | "currencyCode": "USD", 5 | "currencyName": "US Dollar", 6 | "currencySymbol": "$", 7 | "flag": "https://s3.amazonaws.com/rld-flags/us.svg", 8 | "callingCodes": [ 9 | "+1" 10 | ] 11 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/discount/discount.json: -------------------------------------------------------------------------------- 1 | { 2 | "percentage": 13.0, 3 | "internationalPercentage": 13.0, 4 | "localPercentage": 0.0, 5 | "updatedAt": "2020-12-26 06:57:09", 6 | "operator": { 7 | "id": 174, 8 | "operatorId": 174, 9 | "name": "Natcom Haiti", 10 | "countryCode": "HT", 11 | "data": false, 12 | "bundle": false, 13 | "status": true 14 | } 15 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/operator/operator_filtered_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 174, 3 | "operatorId": 174, 4 | "name": "Natcom Haiti", 5 | "bundle": false, 6 | "data": false, 7 | "pin": false, 8 | "supportsLocalAmounts": false, 9 | "denominationType": "RANGE", 10 | "senderCurrencyCode": "USD", 11 | "senderCurrencySymbol": "$", 12 | "destinationCurrencyCode": "HTG", 13 | "destinationCurrencySymbol": "HTG", 14 | "commission": 13.0, 15 | "internationalDiscount": 13.0, 16 | "localDiscount": 0.0, 17 | "mostPopularAmount": 15, 18 | "mostPopularLocalAmount": null, 19 | "minAmount": 0.45, 20 | "maxAmount": 104, 21 | "localMinAmount": null, 22 | "localMaxAmount": null, 23 | "country": { 24 | "isoName": "HT", 25 | "name": "Haiti" 26 | }, 27 | "fx": { 28 | "rate": 67.12, 29 | "currencyCode": "HTG" 30 | }, 31 | "logoUrls": [ 32 | "https://s3.amazonaws.com/rld-operator/c83d4a0e-63d5-4ea3-b738-2e1f59d2b070-size-3.png", 33 | "https://s3.amazonaws.com/rld-operator/c83d4a0e-63d5-4ea3-b738-2e1f59d2b070-size-2.png", 34 | "https://s3.amazonaws.com/rld-operator/c83d4a0e-63d5-4ea3-b738-2e1f59d2b070-size-1.png" 35 | ], 36 | "fixedAmounts": [], 37 | "fixedAmountsDescriptions": {}, 38 | "localFixedAmounts": [], 39 | "localFixedAmountsDescriptions": {}, 40 | "suggestedAmounts": [ 41 | 1, 42 | 5, 43 | 10, 44 | 15, 45 | 20, 46 | 25, 47 | 30, 48 | 35, 49 | 40, 50 | 45, 51 | 50, 52 | 55, 53 | 60, 54 | 65, 55 | 70, 56 | 75, 57 | 80, 58 | 85, 59 | 90, 60 | 95, 61 | 100 62 | ], 63 | "suggestedAmountsMap": { 64 | "1": 67.10, 65 | "5": 335.60, 66 | "10": 671.20, 67 | "15": 1006.80, 68 | "20": 1342.40, 69 | "25": 1678.00, 70 | "30": 2013.60, 71 | "35": 2349.20, 72 | "40": 2684.80, 73 | "45": 3020.40, 74 | "50": 3356.00, 75 | "55": 3691.60, 76 | "60": 4027.20, 77 | "65": 4362.80, 78 | "70": 4698.40, 79 | "75": 5034.00, 80 | "80": 5369.60, 81 | "85": 5705.20, 82 | "90": 6040.80, 83 | "95": 6376.40, 84 | "100": 6712.00 85 | }, 86 | "promotions": [] 87 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/operator/operator_fx_rate.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 174, 3 | "name": "Natcom Haiti", 4 | "fxRate": 335.6, 5 | "currencyCode": "HTG" 6 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/operator/operator_unfiltered_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 174, 3 | "operatorId": 174, 4 | "name": "Natcom Haiti", 5 | "bundle": false, 6 | "data": false, 7 | "pin": false, 8 | "supportsLocalAmounts": false, 9 | "denominationType": "RANGE", 10 | "senderCurrencyCode": "USD", 11 | "senderCurrencySymbol": "$", 12 | "destinationCurrencyCode": "HTG", 13 | "destinationCurrencySymbol": "HTG", 14 | "commission": 13.0, 15 | "internationalDiscount": 13.0, 16 | "localDiscount": 0.0, 17 | "mostPopularAmount": 15, 18 | "mostPopularLocalAmount": null, 19 | "minAmount": 0.45, 20 | "maxAmount": 104, 21 | "localMinAmount": null, 22 | "localMaxAmount": null, 23 | "country": { 24 | "isoName": "HT", 25 | "name": "Haiti" 26 | }, 27 | "fx": { 28 | "rate": 67.12, 29 | "currencyCode": "HTG" 30 | }, 31 | "logoUrls": [ 32 | "https://s3.amazonaws.com/rld-operator/c83d4a0e-63d5-4ea3-b738-2e1f59d2b070-size-3.png", 33 | "https://s3.amazonaws.com/rld-operator/c83d4a0e-63d5-4ea3-b738-2e1f59d2b070-size-2.png", 34 | "https://s3.amazonaws.com/rld-operator/c83d4a0e-63d5-4ea3-b738-2e1f59d2b070-size-1.png" 35 | ], 36 | "fixedAmounts": [], 37 | "fixedAmountsDescriptions": {}, 38 | "localFixedAmounts": [], 39 | "localFixedAmountsDescriptions": {}, 40 | "suggestedAmounts": [ 41 | 1, 42 | 5, 43 | 10, 44 | 15, 45 | 20, 46 | 25, 47 | 30, 48 | 35, 49 | 40, 50 | 45, 51 | 50, 52 | 55, 53 | 60, 54 | 65, 55 | 70, 56 | 75, 57 | 80, 58 | 85, 59 | 90, 60 | 95, 61 | 100 62 | ], 63 | "suggestedAmountsMap": {}, 64 | "promotions": [] 65 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/promotion/promotion_by_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 7016, 3 | "promotionId": 7016, 4 | "operatorId": 121, 5 | "title": "Digicel El Salvador From 07 Feb 2020 00:00 To 31 Dec 2020 23:59 (GMT-06:00)", 6 | "title2": "Bonus 5x", 7 | "description": "

- The promotion will run from 00:01 am Friday, February 7th 2020 through to and including 11:59pm Thursday 31, December 2020 (“The Promotion Period”).
- Customers will be required to send a minimum Top Up of $5 during the promotional period to receive 5 times bonus credit on the Top Up Value plus 30 days Whatsapp Chat:
\"\"- Promotion applies only to prepaid Digicel customers sending Top Up to El Salvador.
- The duration of the bonus credit validity on the recipient’s mobile number is dependent on theTop Up Value (refer to chart above)
- The bonus credit on the recipient’s Digicel mobile number can be used for:
a. Digicel to Digicel calls (on-net calls);
b. Calls to USA
c. Calls to Canada
- Bonus credit is automatically added once Top Up value is received
- To check bonus, customer may dial *120*53#
- Benefit of Whatsapp ONLY includes Whatsapp Chat; Whatsapp calls or video calls are not included
- Postpaid customers are not eligible for the promotion.
- Digicel will not be responsible for: (1) technical failures of any kind, including, but not limited to malfunctions, interruptions, or disconnections in network connections or hardware or software; (2) unauthorized human intervention in any part; or (3) technical or human error which may occur in the administration of this promotion.
- Digicel reserves the right to at any time vary the terms of the promotion, to amend its terms and conditions or to withdraw the promotion. In any of these events, notice will be given via media advertisements or messages to subscribers and will be effective immediately or as of the date referred to in such notifications.
- Digicel reserves the right to terminate, cancel, suspend and/or modify the promotion if any
fraud, virus or other technical problem corrupts the administration, security, safety or proper play of the promotion. In such event, Digicel hereby specifically reserve the right (but not the obligation) to award some other prize hereunder by means of a random drawing from among the eligible entries received up until the time of the impairment.

\"\"

\"\"", 8 | "startDate": "2020-02-07 11:00:00", 9 | "endDate": "2021-01-01 10:59:00", 10 | "denominations": "USD 5 and up", 11 | "localDenominations": null 12 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/report/transaction_history_by_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 10657, 3 | "operatorTransactionId": null, 4 | "customIdentifier": null, 5 | "recipientPhone": "13058989796", 6 | "recipientEmail": null, 7 | "senderPhone": "13056154908", 8 | "countryCode": "US", 9 | "operatorId": 548, 10 | "operatorName": "T-Mobile USA RTR", 11 | "discount": 0, 12 | "discountCurrencyCode": "USD", 13 | "requestedAmount": 20, 14 | "requestedAmountCurrencyCode": "USD", 15 | "deliveredAmount": 20, 16 | "deliveredAmountCurrencyCode": "USD", 17 | "transactionDate": "2021-01-16 01:59:22", 18 | "pinDetail": null, 19 | "balanceInfo": null 20 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/topup/email_topup_transaction.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 10672, 3 | "operatorTransactionId": null, 4 | "customIdentifier": "9497d51e-0591-4d0c-a0a0-93339023ae40", 5 | "recipientPhone": null, 6 | "recipientEmail": "test@nauta.com.cu", 7 | "senderPhone": null, 8 | "countryCode": "CU", 9 | "operatorId": 683, 10 | "operatorName": "Nauta Cuba", 11 | "discount": 1.5, 12 | "discountCurrencyCode": "USD", 13 | "requestedAmount": 30, 14 | "requestedAmountCurrencyCode": "USD", 15 | "deliveredAmount": 772.5, 16 | "deliveredAmountCurrencyCode": "CUP", 17 | "transactionDate": "2021-01-16 03:59:53", 18 | "pinDetail": null, 19 | "balanceInfo": { 20 | "oldBalance": 1617.12, 21 | "newBalance": 1588.62, 22 | "currencyCode": "USD", 23 | "currencyName": "US Dollar", 24 | "updatedAt": "2021-01-16 08:59:53" 25 | } 26 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/topup/email_topup_transaction_async.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 2 3 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/topup/email_topup_transaction_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": null, 3 | "message": null, 4 | "status": "SUCCESSFUL", 5 | "transaction": { 6 | "transactionId": 2, 7 | "status": "SUCCESSFUL", 8 | "operatorTransactionId": null, 9 | "customIdentifier": null, 10 | "recipientPhone": null, 11 | "recipientEmail": "test@nauta.com.cu", 12 | "senderPhone": null, 13 | "countryCode": "CU", 14 | "operatorId": 683, 15 | "operatorName": "Nauta Cuba", 16 | "discount": 1.5, 17 | "discountCurrencyCode": "USD", 18 | "requestedAmount": 30, 19 | "requestedAmountCurrencyCode": "USD", 20 | "deliveredAmount": 772.5, 21 | "deliveredAmountCurrencyCode": "CUP", 22 | "transactionDate": "2022-01-05 15:25:19", 23 | "pinDetail": null, 24 | "balanceInfo": { 25 | "oldBalance": 982.60, 26 | "newBalance": 954.10, 27 | "currencyCode": "USD", 28 | "currencyName": "US Dollar", 29 | "updatedAt": "2022-01-05 20:25:19" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/topup/phone_topup_transaction.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 10670, 3 | "operatorTransactionId": null, 4 | "customIdentifier": "fe8c5a90-691a-452a-a9a6-19045f7f7a7c", 5 | "recipientPhone": "50936377111", 6 | "recipientEmail": null, 7 | "senderPhone": null, 8 | "countryCode": "HT", 9 | "operatorId": 173, 10 | "operatorName": "Digicel Haiti", 11 | "discount": 1.8, 12 | "discountCurrencyCode": "USD", 13 | "requestedAmount": 15, 14 | "requestedAmountCurrencyCode": "USD", 15 | "deliveredAmount": 1089.31, 16 | "deliveredAmountCurrencyCode": "HTG", 17 | "transactionDate": "2021-01-16 03:39:56", 18 | "pinDetail": null, 19 | "balanceInfo": { 20 | "oldBalance": 1648.56, 21 | "newBalance": 1635.36, 22 | "currencyCode": "USD", 23 | "currencyName": "US Dollar", 24 | "updatedAt": "2021-01-16 08:39:56" 25 | } 26 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/topup/phone_topup_transaction_async.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 1 3 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/topup/phone_topup_transaction_status.json: -------------------------------------------------------------------------------- 1 | { 2 | "code": null, 3 | "message": null, 4 | "status": "SUCCESSFUL", 5 | "transaction": { 6 | "transactionId": 1, 7 | "status": "SUCCESSFUL", 8 | "operatorTransactionId": null, 9 | "customIdentifier": null, 10 | "recipientPhone": "50936377111", 11 | "recipientEmail": null, 12 | "senderPhone": null, 13 | "countryCode": "HT", 14 | "operatorId": 174, 15 | "operatorName": "Natcom Haiti", 16 | "discount": 2.6, 17 | "discountCurrencyCode": "USD", 18 | "requestedAmount": 20, 19 | "requestedAmountCurrencyCode": "USD", 20 | "deliveredAmount": 1977.12, 21 | "deliveredAmountCurrencyCode": "HTG", 22 | "transactionDate": "2022-01-05 13:26:20", 23 | "pinDetail": null, 24 | "balanceInfo": { 25 | "oldBalance": 1000.00, 26 | "newBalance": 982.60, 27 | "currencyCode": "USD", 28 | "currencyName": "US Dollar", 29 | "updatedAt": "2022-01-05 18:26:20" 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /java-sdk-airtime/src/test/resources/topup/phone_topup_transaction_with_pin_detail.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 10671, 3 | "operatorTransactionId": null, 4 | "customIdentifier": "ede8ffda-25d7-4039-841c-5f62393dec2b", 5 | "recipientPhone": "306907039456", 6 | "recipientEmail": null, 7 | "senderPhone": null, 8 | "countryCode": "GR", 9 | "operatorId": 672, 10 | "operatorName": "Wind PIN Greece", 11 | "discount": 0.5, 12 | "discountCurrencyCode": "USD", 13 | "requestedAmount": 18.24, 14 | "requestedAmountCurrencyCode": "USD", 15 | "deliveredAmount": 15.04, 16 | "deliveredAmountCurrencyCode": "EUR", 17 | "transactionDate": "2021-01-16 03:44:13", 18 | "pinDetail": { 19 | "serial": "810268", 20 | "info1": "DIAL *611", 21 | "info2": "DIAL *611", 22 | "info3": "Dial *233* and PIN #", 23 | "value": null, 24 | "code": "773709732524155", 25 | "ivr": "1-888-888-8888", 26 | "validity": "90 days" 27 | }, 28 | "balanceInfo": { 29 | "oldBalance": 1635.36, 30 | "newBalance": 1617.12, 31 | "currencyCode": "USD", 32 | "currencyName": "US Dollar", 33 | "updatedAt": "2021-01-16 08:44:13" 34 | } 35 | } -------------------------------------------------------------------------------- /java-sdk-airtime/usage/ACCOUNT-OPERATIONS.md: -------------------------------------------------------------------------------- 1 | # Account Operations 2 | 3 | Use the account operations to perform account related actions 4 | 5 | ## Retrieve account balance info 6 | 7 | ```java 8 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 9 | .clientId(clientId) 10 | .clientSecret(clientSecret) 11 | .environment(Environment.SANDBOX) 12 | .build(); 13 | 14 | Request request; 15 | try { 16 | request = airtimeAPI.accounts().getBalance(); 17 | } catch (ReloadlyException e) { 18 | // api error retrieving access_token 19 | } 20 | 21 | AccountBalanceInfo accountBalanceInfo = null; 22 | try { 23 | accountBalanceInfo = request.execute(); 24 | } catch (APIException e) { 25 | // api error 26 | } catch (ReloadlyException e) { 27 | // request error 28 | } catch (Exception e) { 29 | // all others 30 | } 31 | ``` -------------------------------------------------------------------------------- /java-sdk-airtime/usage/COUNTRY-OPERATIONS.md: -------------------------------------------------------------------------------- 1 | # Countries Operations 2 | 3 | Reloadly supports 140+ Countries around the globe. You can get a list of all or specific supported countries. The 4 | response will give you a list with complete details, iso, flag as well as calling codes for each country. You can also 5 | further filter the countries by getting details for a specific country by its ISO-Alpha2 code. 6 | See https://www.nationsonline.org/oneworld/country_code_list.htm for more details regarding country ISO codes. 7 | 8 | ## Countries - List 9 | 10 | ```java 11 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 12 | .clientId(clientId) 13 | .clientSecret(clientSecret) 14 | .environment(Environment.SANDBOX) 15 | .build(); 16 | 17 | Request> request; 18 | try { 19 | request = airtimeAPI.countries().list(); 20 | } catch (ReloadlyException e) { 21 | // api error retrieving access_token 22 | } 23 | 24 | List countries = null; 25 | try { 26 | countries = request.execute(); 27 | } catch (APIException e) { 28 | // api error 29 | } catch (ReloadlyException e) { 30 | // request error 31 | } catch (Exception e) { 32 | // all others 33 | } 34 | ``` 35 | 36 | ## Countries - Get by country ISO code 37 | 38 | ```java 39 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 40 | .clientId(clientId) 41 | .clientSecret(clientSecret) 42 | .environment(Environment.SANDBOX) 43 | .build(); 44 | 45 | Request request; 46 | 47 | try { 48 | request = airtimeAPI.countries().getByCode(CountryCode.CO); 49 | } catch (ReloadlyException e) { 50 | // api error retrieving access_token 51 | } 52 | 53 | Country country = null; 54 | try { 55 | country = request.execute(); 56 | } catch (APIException e) { 57 | // api error 58 | } catch (ReloadlyException e) { 59 | // request error 60 | } catch (Exception e) { 61 | // all others 62 | } 63 | ``` -------------------------------------------------------------------------------- /java-sdk-airtime/usage/DISCOUNT-OPERATIONS.md: -------------------------------------------------------------------------------- 1 | # Discounts Operations 2 | 3 | Discounts or commissions are a way for you to check what percentage discount rate will you get for each operator when 4 | you send a successful top-up. These operations can be used to calculate your profits. All Commissions are paid instantly 5 | when a top-up is processed. 6 | 7 | One thing to note on the response is that All Operators provide two types of discounts, One is the international 8 | discount, and the other is the local discount. These are returned as the internationalPercentage and localPercentage 9 | fields in the response object. Depending on your account currency the country you are sending the topup to, you are 10 | eligible for either one of these discounts. For example if you're sending from the US to Canada you will be eligible for 11 | the international discount for the canadian operator. While sending within the same country you will be eligible for the 12 | local discount of the operator **if available**. Note that, local discount may not always be available; in which case 13 | the international discount will be applied. 14 | 15 | ## Discounts - List 16 | 17 | ```java 18 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 19 | .clientId(clientId) 20 | .clientSecret(clientSecret) 21 | .environment(Environment.SANDBOX) 22 | .build(); 23 | 24 | Request> request; 25 | try { 26 | request = airtimeAPI.discounts().list(); 27 | } catch (ReloadlyException e) { 28 | // api error retrieving access_token 29 | } 30 | 31 | Page discountPage = null; 32 | try { 33 | discountPage = request.execute(); 34 | } catch (APIException e) { 35 | // api error 36 | } catch (ReloadlyException e) { 37 | // request error 38 | } catch (Exception e) { 39 | // all others 40 | } 41 | ``` 42 | 43 | ## Discount - List with filters 44 | 45 | ```java 46 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 47 | .clientId(clientId) 48 | .clientSecret(clientSecret) 49 | .environment(Environment.SANDBOX) 50 | .build(); 51 | 52 | int pageNumber = 1; 53 | int pageSize = 5; 54 | QueryFilter filter = new QueryFilter().withPage(pageNumber, pageSize); 55 | Request> request; 56 | 57 | try { 58 | request = airtimeAPI.discounts().list(filter); 59 | } catch (ReloadlyException e) { 60 | // api error retrieving access_token 61 | } 62 | 63 | Page discountPage = null; 64 | try { 65 | discountPage = request.execute(); 66 | } catch (APIException e) { 67 | // api error 68 | } catch (ReloadlyException e) { 69 | // request error 70 | } catch (Exception e) { 71 | // all others 72 | } 73 | ``` 74 | 75 | ## Discounts - Get by operator id 76 | 77 | ```java 78 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 79 | .clientId(clientId) 80 | .clientSecret(clientSecret) 81 | .environment(Environment.SANDBOX) 82 | .build(); 83 | 84 | Long operatorId = 174L; //From Operator.id 85 | Request request; 86 | 87 | try { 88 | request = airtimeAPI.discounts().getByOperatorId(operatorId); 89 | } catch (ReloadlyException e) { 90 | // api error retrieving access_token 91 | } 92 | 93 | Discount discount = null; 94 | try { 95 | discount = request.execute(); 96 | } catch (APIException e) { 97 | // api error 98 | } catch (ReloadlyException e) { 99 | // request error 100 | } catch (Exception e) { 101 | // all others 102 | } 103 | ``` -------------------------------------------------------------------------------- /java-sdk-airtime/usage/REPORT-OPERATIONS.md: -------------------------------------------------------------------------------- 1 | # Report Operations 2 | 3 | Retrieve various reports such as transaction history etc... 4 | 5 | ## Reports - List transaction history 6 | 7 | ```java 8 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 9 | .clientId(clientId) 10 | .clientSecret(clientSecret) 11 | .environment(Environment.SANDBOX) 12 | .build(); 13 | 14 | Request> request; 15 | 16 | try { 17 | request = airtimeAPI.reports().transactionsHistory().list(); 18 | } catch (ReloadlyException e) { 19 | // api error retrieving access_token 20 | } 21 | 22 | Page transactionHistoryPage = null; 23 | try { 24 | transactionHistoryPage = request.execute(); 25 | } catch (APIException e) { 26 | // api error 27 | } catch (ReloadlyException e) { 28 | // request error 29 | } catch (Exception e) { 30 | // all others 31 | } 32 | ``` 33 | 34 | ## Reports - List transaction history with filters 35 | 36 | ```java 37 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 38 | .clientId(clientId) 39 | .clientSecret(clientSecret) 40 | .environment(Environment.SANDBOX) 41 | .build(); 42 | 43 | LocalDateTime startDate = LocalDateTime.now().withMonth(1).withDayOfMonth(1).withHour(0).withMinute(0).withSecond(0); 44 | LocalDateTime endDate = LocalDateTime.now().withHour(23).withMinute(59).withSecond(59); 45 | 46 | TransactionHistoryFilter filter = new TransactionHistoryFilter().withPage(1, 5) 47 | .startDate(startDate) 48 | .endDate(endDate); 49 | 50 | //Additional filters are available 51 | /*.operatorId(operatorId) 52 | .countryCode(CountryCode.HT) 53 | .operatorName("Digicel Haiti") 54 | .customIdentifier("Your-Transaction-Custom-Identifier");*/ 55 | 56 | Request> request; 57 | 58 | try { 59 | request = airtimeAPI.reports().transactionsHistory().list(filter); 60 | } catch (ReloadlyException e) { 61 | // api error retrieving access_token 62 | } 63 | 64 | Page transactionHistoryPage = null; 65 | try { 66 | transactionHistoryPage = request.execute(); 67 | } catch (APIException e) { 68 | // api error 69 | } catch (ReloadlyException e) { 70 | // request error 71 | } catch (Exception e) { 72 | // all others 73 | } 74 | ``` 75 | 76 | ## Reports - Get transaction history by id 77 | 78 | ```java 79 | AirtimeAPI airtimeAPI = AirtimeAPI.builder() 80 | .clientId(clientId) 81 | .clientSecret(clientSecret) 82 | .environment(Environment.SANDBOX) 83 | .build(); 84 | 85 | Long transactionId = 10658L;//From Transaction.id 86 | Request request; 87 | 88 | try { 89 | request = airtimeAPI.reports().transactionsHistory().getById(transactionId); 90 | } catch (ReloadlyException e) { 91 | // api error retrieving access_token 92 | } 93 | 94 | TopupTransaction transaction = null; 95 | try { 96 | transaction = request.execute(); 97 | } catch (APIException e) { 98 | // api error 99 | } catch (ReloadlyException e) { 100 | // request error 101 | } catch (Exception e) { 102 | // all others 103 | } 104 | ``` -------------------------------------------------------------------------------- /java-sdk-authentication/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | 26 | # OS generated files # 27 | ###################### 28 | .DS_Store 29 | .DS_Store? -------------------------------------------------------------------------------- /java-sdk-authentication/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | software.reloadly 9 | java-sdk 10 | 1.0.3 11 | 12 | 13 | java-sdk-authentication 14 | 1.0.3 15 | Reloadly Java SDK :: Services :: Reloadly Authentication Service 16 | https://github.com/reloadly/reloadly-sdk-java 17 | 18 | Reloadly Java SDK for the Authentication API. The module holds the client classes 19 | that are used for communicating with Reloadly Authentication Service. 20 | 21 | 22 | 23 | 24 | software.reloadly 25 | java-sdk-core 26 | false 27 | 28 | 29 | 30 | 31 | 32 | 33 | org.jacoco 34 | jacoco-maven-plugin 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /java-sdk-authentication/src/main/java/software/reloadly/sdk/authentication/client/AuthenticationAPI.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.authentication.client; 2 | 3 | import lombok.Builder; 4 | import okhttp3.HttpUrl; 5 | import org.apache.maven.model.Model; 6 | import org.apache.maven.model.io.xpp3.MavenXpp3Reader; 7 | import software.reloadly.sdk.core.enums.Service; 8 | import software.reloadly.sdk.core.internal.enums.Version; 9 | import software.reloadly.sdk.core.internal.net.API; 10 | import software.reloadly.sdk.core.internal.util.Asserter; 11 | import software.reloadly.sdk.core.net.HttpOptions; 12 | 13 | import java.io.FileReader; 14 | import java.util.List; 15 | 16 | import static org.apache.commons.lang3.StringUtils.isBlank; 17 | 18 | /** 19 | * Class that provides an implementation of some Authentication and Authorization API methods 20 | */ 21 | public class AuthenticationAPI extends API { 22 | 23 | private static final String BASE_URL = "https://auth.reloadly.com"; 24 | 25 | private final HttpUrl baseUrl; 26 | private final Service service; 27 | 28 | @SuppressWarnings("unused") 29 | @Builder 30 | public AuthenticationAPI(String clientId, String clientSecret, 31 | Service service, boolean enableLogging, 32 | List redactHeaders, HttpOptions options, Boolean enableTelemetry) { 33 | 34 | super(clientId, clientSecret, enableLogging, redactHeaders, options, enableTelemetry, getSDKVersion(), 35 | Version.AUTHENTICATION_V1.getValue() 36 | ); 37 | 38 | Asserter.assertNotBlank(clientId, "Client id"); 39 | Asserter.assertNotBlank(clientSecret, "Client secret"); 40 | Asserter.assertNotNull(service, "Target service"); 41 | this.service = service; 42 | 43 | baseUrl = createUrlFromString(BASE_URL); 44 | if (baseUrl == null) { 45 | throw new IllegalArgumentException( 46 | "The oauth base url had an invalid format and couldn't be parsed as an URL." 47 | ); 48 | } 49 | } 50 | 51 | public OAuth2ClientCredentialsOperation clientCredentials() { 52 | return new OAuth2ClientCredentialsOperation(baseUrl, clientId, service, clientSecret, client); 53 | } 54 | 55 | private static String getSDKVersion() { 56 | MavenXpp3Reader reader = new MavenXpp3Reader(); 57 | Model model; 58 | try { 59 | model = reader.read(new FileReader("./java-sdk-authentication/pom.xml")); 60 | } catch (Exception e) { 61 | try { 62 | model = reader.read(new FileReader("../java-sdk-authentication/pom.xml")); 63 | } catch (Exception ex) { 64 | return "MISSING"; 65 | } 66 | 67 | if (model == null) { 68 | return "MISSING"; 69 | } 70 | } 71 | 72 | return isBlank(model.getVersion()) ? "MISSING" : model.getVersion(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /java-sdk-authentication/src/main/java/software/reloadly/sdk/authentication/client/OAuth2ClientCredentialsOperation.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.authentication.client; 2 | 3 | import software.reloadly.sdk.authentication.dto.request.OAuth2ClientCredentialsRequest; 4 | import software.reloadly.sdk.authentication.dto.request.TokenRequest; 5 | import software.reloadly.sdk.core.internal.constant.GrantType; 6 | import software.reloadly.sdk.core.internal.constant.HttpHeader; 7 | import software.reloadly.sdk.core.internal.constant.MediaType; 8 | import software.reloadly.sdk.core.enums.Service; 9 | import software.reloadly.sdk.core.internal.util.Asserter; 10 | import lombok.AllArgsConstructor; 11 | import okhttp3.HttpUrl; 12 | import okhttp3.OkHttpClient; 13 | 14 | @AllArgsConstructor 15 | public class OAuth2ClientCredentialsOperation { 16 | 17 | private static final String KEY_CLIENT_ID = "client_id"; 18 | private static final String KEY_CLIENT_SECRET = "client_secret"; 19 | private static final String KEY_GRANT_TYPE = "grant_type"; 20 | private static final String KEY_AUDIENCE = "audience"; 21 | 22 | private static final String PATH_OAUTH = "oauth"; 23 | private static final String PATH_TOKEN = "token"; 24 | 25 | private final HttpUrl baseUrl; 26 | private final String clientId; 27 | private final Service service; 28 | private final String clientSecret; 29 | private final OkHttpClient client; 30 | 31 | /** 32 | * Creates a request to get a Token for the given audience using the 'Client Credentials' grant. 33 | *
34 |      * {@code
35 |      * AuthAPI auth = new AuthenticationAPI("B3c6RYhk1v9SbIJcRIOwu62gIUGsnze", "2679NfkaBn62e6w5E8zNEzjr-yWfkaBne");
36 |      * try {
37 |      *      TokenHolder result = auth.requestToken("https://topups.reloadly.com").execute();
38 |      * } catch (APIException e) {
39 |      *      //api error
40 |      * } catch (ReloadlyException e) {
41 |      *     // request error
42 |      * } catch (Exception e) {
43 |      *     // all other errors
44 |      * }
45 |      * }
46 |      * 
47 | * 48 | * @return a Request to configure and execute. 49 | */ 50 | public OAuth2ClientCredentialsRequest getAccessToken() { 51 | 52 | Asserter.assertNotNull(service, "Service"); 53 | String audience = service.getServiceUrl(); 54 | 55 | if (!audience.startsWith("https://") || audience.startsWith("http://")) { 56 | audience = "https://" + audience; 57 | } 58 | 59 | String url = baseUrl.newBuilder().addPathSegment(PATH_OAUTH).addPathSegment(PATH_TOKEN).build().toString(); 60 | 61 | TokenRequest request = new TokenRequest(client, url); 62 | request.addParameter(KEY_CLIENT_ID, clientId) 63 | .addParameter(KEY_CLIENT_SECRET, clientSecret) 64 | .addParameter(KEY_GRANT_TYPE, GrantType.CLIENT_CREDENTIALS) 65 | .addParameter(KEY_AUDIENCE, audience) 66 | .addHeader(HttpHeader.ACCEPT, MediaType.APPLICATION_JSON) 67 | .addHeader(HttpHeader.CONTENT_TYPE, MediaType.APPLICATION_JSON); 68 | 69 | return request; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /java-sdk-authentication/src/main/java/software/reloadly/sdk/authentication/dto/request/OAuth2ClientCredentialsRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.authentication.dto.request; 2 | 3 | 4 | import software.reloadly.sdk.authentication.dto.response.TokenHolder; 5 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 6 | 7 | /** 8 | * Class that represents an OAuth 2.0 Authentication/Authorization request. The execution will return a {@link TokenHolder}. 9 | */ 10 | public interface OAuth2ClientCredentialsRequest extends Request { 11 | 12 | /** 13 | * Setter for the audience value to request 14 | * 15 | * @param audience the audience to request 16 | * @return this request instance. 17 | */ 18 | OAuth2ClientCredentialsRequest setAudience(String audience); 19 | } 20 | -------------------------------------------------------------------------------- /java-sdk-authentication/src/main/java/software/reloadly/sdk/authentication/dto/request/TokenRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.authentication.dto.request; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import software.reloadly.sdk.authentication.dto.response.TokenHolder; 5 | import software.reloadly.sdk.core.internal.dto.request.CustomRequest; 6 | import okhttp3.OkHttpClient; 7 | 8 | public class TokenRequest extends CustomRequest implements OAuth2ClientCredentialsRequest { 9 | 10 | public TokenRequest(OkHttpClient client, String url) { 11 | super(client, url, "POST", new TypeReference() { 12 | }); 13 | } 14 | 15 | @Override 16 | public TokenRequest setAudience(String audience) { 17 | super.addParameter("audience", audience); 18 | return this; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /java-sdk-authentication/src/main/java/software/reloadly/sdk/authentication/dto/response/TokenHolder.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.authentication.dto.response; 2 | 3 | 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import software.reloadly.sdk.authentication.client.AuthenticationAPI; 8 | 9 | /** 10 | * Class that contains the Tokens obtained after a call to the {@link AuthenticationAPI} methods. 11 | */ 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class TokenHolder { 15 | 16 | @JsonProperty("access_token") 17 | private String accessToken; 18 | 19 | @JsonProperty("token_type") 20 | private String tokenType; 21 | 22 | @JsonProperty("expires_in") 23 | private long expiresIn; 24 | 25 | /** 26 | * Getter for the Reloadly's access token. 27 | * 28 | * @return the access token or null if missing. 29 | */ 30 | public String getToken() { 31 | return accessToken; 32 | } 33 | 34 | /** 35 | * Getter for the token type. 36 | * 37 | * @return the token type or null if missing. 38 | */ 39 | public String getTokenType() { 40 | return tokenType; 41 | } 42 | 43 | /** 44 | * Getter for the expiration time ('exp' claim) of the recently issued token. 45 | * 46 | * @return the expiration time or null if missing. 47 | */ 48 | public long getExpiresIn() { 49 | return expiresIn; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /java-sdk-authentication/src/test/java/software/reloadly/sdk/authentication/AuthenticationAPIMockServer.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.authentication; 2 | 3 | import com.fasterxml.jackson.databind.ObjectMapper; 4 | import com.fasterxml.jackson.databind.type.MapType; 5 | import okhttp3.mockwebserver.MockResponse; 6 | import okhttp3.mockwebserver.MockWebServer; 7 | import okhttp3.mockwebserver.RecordedRequest; 8 | import okio.Buffer; 9 | 10 | import java.io.IOException; 11 | import java.nio.file.Files; 12 | import java.nio.file.Paths; 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | 16 | public class AuthenticationAPIMockServer { 17 | 18 | private final MockWebServer server; 19 | 20 | public AuthenticationAPIMockServer() throws Exception { 21 | server = new MockWebServer(); 22 | server.start(); 23 | } 24 | 25 | public void stop() throws IOException { 26 | server.shutdown(); 27 | } 28 | 29 | public String getBaseUrl() { 30 | return server.url("").toString().replaceAll("/\\z", ""); 31 | } 32 | 33 | public RecordedRequest takeRequest() throws InterruptedException { 34 | return server.takeRequest(); 35 | } 36 | 37 | private String readTextFile(String path) throws IOException { 38 | return new String(Files.readAllBytes(Paths.get(path))); 39 | } 40 | 41 | public void jsonResponse(String path, int statusCode) throws IOException { 42 | MockResponse response = new MockResponse() 43 | .setResponseCode(statusCode) 44 | .addHeader("Content-Type", "application/json") 45 | .setBody(readTextFile(path)); 46 | server.enqueue(response); 47 | } 48 | 49 | public static Map bodyFromRequest(RecordedRequest request) throws IOException { 50 | ObjectMapper mapper = new ObjectMapper(); 51 | MapType mapType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, Object.class); 52 | try (Buffer body = request.getBody()) { 53 | return mapper.readValue(body.inputStream(), mapType); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /java-sdk-authentication/src/test/resources/client/auth/error_access_denied.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeStamp": "2020-12-27 08:26:25", 3 | "message": "Access Denied", 4 | "path": "/oauth/token", 5 | "errorCode": null, 6 | "infoLink": null, 7 | "details": [] 8 | } -------------------------------------------------------------------------------- /java-sdk-authentication/src/test/resources/client/auth/error_invalid_audience.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeStamp": "2020-12-27 08:26:25", 3 | "message": "The provided audience is not valid", 4 | "path": "/oauth/token", 5 | "errorCode": "INVALID_AUDIENCE", 6 | "infoLink": null, 7 | "details": [] 8 | } -------------------------------------------------------------------------------- /java-sdk-authentication/src/test/resources/client/auth/success_token_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "access_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJPbmxpbmUgSldUIEJ1aWxkZXIiLCJpYXQiOjE1NjA4MjQxNDUsImV4cCI6MTU5MjM2MDE0NSwiYXVkIjoid3d3LmV4YW1wbGUuY29tIiwic3ViIjoianJvY2tldEBleGFtcGxlLmNvbSIsIkdpdmVuTmFtZSI6IkpvaG5ueSIsIlN1cm5hbWUiOiJSb2NrZXQiLCJFbWFpbCI6Impyb2NrZXRAZXhhbXBsZS5jb20iLCJSb2xlIjpbIk1hbmFnZXIiLCJQcm9qZWN0IEFkbWluaXN0cmF0b3IiXX0.nWH9TcLnMhrOLn-UC_3Abn3HGoJxs3Y7MDIX65vO7Xg", 3 | "expires_in": 5184000, 4 | "token_type": "Bearer" 5 | } -------------------------------------------------------------------------------- /java-sdk-authentication/src/test/resources/client/error_invalid_token.json: -------------------------------------------------------------------------------- 1 | { 2 | "timeStamp": "2020-12-27 08:26:25", 3 | "message": "Invalid token, make sure token is valid for the given environment", 4 | "path": "/phone-intel", 5 | "errorCode": "INVALID_TOKEN", 6 | "infoLink": null, 7 | "details": [] 8 | } -------------------------------------------------------------------------------- /java-sdk-core/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | 26 | # OS generated files # 27 | ###################### 28 | .DS_Store 29 | .DS_Store? -------------------------------------------------------------------------------- /java-sdk-core/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | 8 | software.reloadly 9 | java-sdk 10 | 1.0.3 11 | 12 | 13 | java-sdk-core 14 | 1.0.3 15 | Reloadly Java SDK :: Core 16 | https://github.com/reloadly/reloadly-sdk-java 17 | 18 | The Reloadly SDK for Java - Core module holds the classes that are used by the individual 19 | service clients to interact with Reloadly Services. Users need to depend on reloadly-java-sdk artifact 20 | for accessing individual client classes. 21 | 22 | 23 | 24 | 3.18.2 25 | 2.6.0 26 | 3.12.0 27 | 2.22.2 28 | 1.7 29 | 30 | 31 | 32 | 33 | org.springframework.data 34 | spring-data-commons 35 | ${spring-data-commons.version} 36 | 37 | 38 | com.auth0 39 | java-jwt 40 | ${java-jwt.version} 41 | 42 | 43 | org.apache.commons 44 | commons-lang3 45 | ${apache-commons-lang.version} 46 | 47 | 48 | commons-validator 49 | commons-validator 50 | ${apache-commons-validator.version} 51 | 52 | 53 | com.squareup.okhttp3 54 | mockwebserver 55 | compile 56 | 57 | 58 | 59 | 60 | 61 | 62 | org.jacoco 63 | jacoco-maven-plugin 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/constant/ServiceURLs.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.constant; 2 | 3 | public class ServiceURLs { 4 | 5 | public static final String AIRTIME = "https://topups.reloadly.com"; 6 | public static final String GIFTCARD = "https://giftcards.reloadly.com"; 7 | public static final String AIRTIME_SANDBOX = "https://topups-sandbox.reloadly.com"; 8 | public static final String GIFTCARD_SANDBOX = "https://giftcards-sandbox.reloadly.com"; 9 | 10 | } 11 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/dto/APIError.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.dto; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Date; 9 | import java.util.List; 10 | 11 | 12 | /** 13 | * Class that represents a Reloadly Server error captured from a http response. Provides different methods to get a clue of why the request failed. 14 | * i.e.: 15 | *
16 |  * {@code
17 |  * {
18 |  *      "details": [],
19 |  *      "errorCode": null
20 |  *      "message": "Invalid operator id provided",
21 |  *      "path": "/operators/68695596",
22 |  *      "timeStamp": 1559108814252,
23 |  * }
24 |  * }
25 |  * 
26 | */ 27 | @Getter 28 | @Setter 29 | @EqualsAndHashCode 30 | public class APIError { 31 | 32 | 33 | /** 34 | * Additional details that might be helpful in understanding the error(s) that occurred. 35 | */ 36 | private List details = new ArrayList<>(); 37 | 38 | /** 39 | * For some errors that could be handled programmatically, a string summarizing the error reported. 40 | */ 41 | private String errorCode; 42 | 43 | /** 44 | * Link to documentations containing additional info regarding the error. 45 | */ 46 | private String infoLink; 47 | 48 | /** 49 | * A human-readable message providing more details about the error. 50 | */ 51 | private String message; 52 | 53 | /** 54 | * The end-point that was used when the error occurred 55 | */ 56 | private String path; 57 | 58 | /** 59 | * "The timestamp when the usage occurred" 60 | */ 61 | private Date timeStamp; 62 | 63 | 64 | public APIError(String message, String path, Date timeStamp) { 65 | this.message = message; 66 | this.path = path; 67 | this.timeStamp = timeStamp; 68 | } 69 | 70 | public APIError(String message, String path, Date timeStamp, String errorCode) { 71 | this.message = message; 72 | this.path = path; 73 | this.timeStamp = timeStamp; 74 | this.errorCode = errorCode; 75 | } 76 | 77 | public APIError(String message, String path, Date timeStamp, String errorCode, List details) { 78 | this.message = message; 79 | this.path = path; 80 | this.timeStamp = timeStamp; 81 | this.errorCode = errorCode; 82 | this.details = details; 83 | } 84 | 85 | public APIError(String message, String path, Date timeStamp, String errorCode, List details, String infoLink) { 86 | this.message = message; 87 | this.path = path; 88 | this.timeStamp = timeStamp; 89 | this.errorCode = errorCode; 90 | this.details = details; 91 | this.infoLink = infoLink; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/dto/response/Page.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonCreator; 4 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 5 | import com.fasterxml.jackson.annotation.JsonInclude; 6 | import com.fasterxml.jackson.annotation.JsonProperty; 7 | import com.fasterxml.jackson.databind.JsonNode; 8 | import org.springframework.data.domain.PageImpl; 9 | import org.springframework.data.domain.PageRequest; 10 | import org.springframework.data.domain.Pageable; 11 | 12 | import java.util.List; 13 | 14 | @SuppressWarnings("unused") 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | @JsonInclude(JsonInclude.Include.NON_NULL) 17 | public class Page extends PageImpl { 18 | 19 | @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) 20 | public Page(@JsonProperty("content") List content, 21 | @JsonProperty("number") int number, 22 | @JsonProperty("size") int size, 23 | @JsonProperty("totalElements") Long totalElements, 24 | @JsonProperty("pageable") JsonNode pageable, 25 | @JsonProperty("last") boolean last, 26 | @JsonProperty("totalPages") int totalPages, 27 | @JsonProperty("sort") JsonNode sort, 28 | @JsonProperty("first") boolean first, 29 | @JsonProperty("numberOfElements") int numberOfElements) { 30 | 31 | super(content, PageRequest.of(number, size), totalElements); 32 | } 33 | 34 | public Page(List content, Pageable pageable, long total) { 35 | super(content, pageable, total); 36 | } 37 | 38 | public Page(List content) { 39 | super(content); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/enums/Environment.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.enums; 2 | 3 | public enum Environment { 4 | LIVE, 5 | SANDBOX 6 | } 7 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/enums/Service.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.enums; 2 | 3 | import software.reloadly.sdk.core.constant.ServiceURLs; 4 | import lombok.Getter; 5 | import lombok.RequiredArgsConstructor; 6 | 7 | @Getter 8 | @RequiredArgsConstructor 9 | public enum Service { 10 | GIFTCARD(ServiceURLs.GIFTCARD, ServiceURLs.GIFTCARD), 11 | GIFTCARD_SANDBOX(ServiceURLs.GIFTCARD_SANDBOX, ServiceURLs.GIFTCARD_SANDBOX), 12 | AIRTIME(ServiceURLs.AIRTIME, "https://topups-hs256.reloadly.com"), 13 | AIRTIME_SANDBOX(ServiceURLs.AIRTIME_SANDBOX, "https://topups-hs256-sandbox.reloadly.com"); 14 | 15 | private final String serviceUrl; 16 | private final String serviceAudience; 17 | } 18 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/exception/APIException.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.exception; 2 | 3 | import lombok.EqualsAndHashCode; 4 | import lombok.Getter; 5 | import lombok.Setter; 6 | 7 | import java.util.ArrayList; 8 | import java.util.Date; 9 | import java.util.List; 10 | 11 | 12 | /** 13 | * Class that represents a Reloadly Server error captured from a http response. Provides different methods to get a clue of why the request failed. 14 | * i.e.: 15 | *
 16 |  * {@code
 17 |  * {
 18 |  *      "details": [],
 19 |  *      "errorCode": null
 20 |  *      "httpStatusCode": 400,
 21 |  *      "message": "Invalid operator id provided",
 22 |  *      "path": "/operators/68695596",
 23 |  *      "timeStamp": 1559108814252,
 24 |  * }
 25 |  * }
 26 |  * 
27 | */ 28 | @Getter 29 | @Setter 30 | @EqualsAndHashCode(callSuper = true) 31 | public class APIException extends ReloadlyException { 32 | 33 | 34 | /** 35 | * Additional details that might be helpful in understanding the error(s) that occurred. 36 | */ 37 | private List details = new ArrayList<>(); 38 | 39 | /** 40 | * For some errors that could be handled programmatically, a string summarizing the error reported. 41 | */ 42 | private String errorCode; 43 | 44 | /** 45 | * HTTP status indicate whether a specific HTTP request has been successfully completed. 46 | * Responses are grouped in five classes: informational responses, successful responses, 47 | * redirects, client errors, and servers errors. 48 | * See https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html 49 | */ 50 | private int httpStatusCode; 51 | 52 | /** 53 | * The end-point that was used when the error occurred 54 | */ 55 | private String path; 56 | 57 | /** 58 | * "The timestamp when the usage occurred" 59 | */ 60 | private Date timeStamp; 61 | 62 | 63 | public APIException(String message, int httpStatusCode, String path) { 64 | super(message); 65 | this.path = path; 66 | this.httpStatusCode = httpStatusCode; 67 | timeStamp = new Date(); 68 | } 69 | 70 | public APIException(String message, int httpStatusCode, String path, Throwable cause) { 71 | super(message, cause); 72 | this.path = path; 73 | this.httpStatusCode = httpStatusCode; 74 | } 75 | 76 | public APIException(String message, String path, int httpStatusCode, String errorCode) { 77 | super(message); 78 | this.path = path; 79 | this.httpStatusCode = httpStatusCode; 80 | this.errorCode = errorCode; 81 | } 82 | 83 | public APIException(String message, String path, int httpStatusCode, String errorCode, Throwable cause) { 84 | super(message, cause); 85 | this.path = path; 86 | this.httpStatusCode = httpStatusCode; 87 | this.errorCode = errorCode; 88 | } 89 | 90 | public APIException(String message, String path, int httpStatusCode, String errorCode, List details) { 91 | super(message); 92 | this.path = path; 93 | this.httpStatusCode = httpStatusCode; 94 | this.errorCode = errorCode; 95 | this.details = details; 96 | } 97 | 98 | public APIException(String message, String path, int httpStatusCode, String errorCode, List details, Throwable cause) { 99 | super(message, cause); 100 | this.path = path; 101 | this.httpStatusCode = httpStatusCode; 102 | this.errorCode = errorCode; 103 | this.details = details; 104 | } 105 | 106 | public APIException(String message) { 107 | super(message); 108 | } 109 | 110 | public APIException(String message, Throwable cause) { 111 | super(message, cause); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/exception/RateLimitException.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.exception; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Represents a server error when a rate limit has been exceeded. 7 | *

8 | * Getters for {@code limit, remaining} and {@code reset} corresponds to {@code X-RateLimit-Limit, X-RateLimit-Remaining} and {@code X-RateLimit-Reset} HTTP headers. 9 | * If the value of any headers is missing, then a default value -1 will be assigned. 10 | *

11 | * To learn more about rate limits, visit https://api.reloadly.com/docs/policies/rate-limits 12 | */ 13 | @SuppressWarnings("unused") 14 | public class RateLimitException extends APIException { 15 | 16 | private long limit; 17 | 18 | private long remaining; 19 | 20 | private long expectedResetTimestamp; 21 | 22 | private static final int STATUS_CODE_TOO_MANY_REQUEST = 429; 23 | 24 | public RateLimitException(String message, String path, int httpStatusCode, String errorCode) { 25 | super(message, path, httpStatusCode, errorCode); 26 | } 27 | 28 | public RateLimitException(String message, String path, int httpStatusCode, String errorCode, List details) { 29 | super(message, path, httpStatusCode, errorCode, details); 30 | } 31 | 32 | /** 33 | * Getter for the maximum number of requests available in the current time frame. 34 | * 35 | * @return The maximum number of requests or -1 if missing. 36 | */ 37 | public long getLimit() { 38 | return limit; 39 | } 40 | 41 | public void setLimit(long limit) { 42 | this.limit = limit; 43 | } 44 | 45 | /** 46 | * Getter for the number of remaining requests in the current time frame. 47 | * 48 | * @return Number of remaining requests or -1 if missing. 49 | */ 50 | public long getRemaining() { 51 | return remaining; 52 | } 53 | 54 | public void setRemaining(long remaining) { 55 | this.remaining = remaining; 56 | } 57 | 58 | /** 59 | * Getter for the UNIX timestamp of the expected time when the rate limit will reset. 60 | * 61 | * @return The UNIX timestamp or -1 if missing. 62 | */ 63 | public long getExpectedResetTimestamp() { 64 | return expectedResetTimestamp; 65 | } 66 | 67 | public void setExpectedResetTimestamp(long expectedResetTimestamp) { 68 | this.expectedResetTimestamp = expectedResetTimestamp; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/exception/ReloadlyException.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.exception; 2 | 3 | import java.io.IOException; 4 | 5 | /** 6 | * Class that represents an error captured when executing a http request to the Reloadly Server. 7 | */ 8 | public class ReloadlyException extends IOException { 9 | 10 | public ReloadlyException(String message){ 11 | super(message); 12 | } 13 | 14 | public ReloadlyException(String message, Throwable cause){ 15 | super(message, cause); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/exception/oauth/OAuthException.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.exception.oauth; 2 | 3 | 4 | import org.apache.commons.lang3.StringUtils; 5 | import software.reloadly.sdk.core.exception.APIException; 6 | 7 | import java.util.List; 8 | 9 | public class OAuthException extends APIException { 10 | 11 | public OAuthException(String message, int httpStatusCode, String path) { 12 | super(message, httpStatusCode, path); 13 | } 14 | 15 | public OAuthException(String message, int httpStatusCode, String path, Throwable cause) { 16 | super(message, httpStatusCode, path, cause); 17 | } 18 | 19 | public OAuthException(String message, String path, int httpStatusCode, String errorCode) { 20 | super(message, path, httpStatusCode, errorCode); 21 | } 22 | 23 | public OAuthException(String message, String path, int httpStatusCode, String errorCode, Throwable cause) { 24 | super(message, path, httpStatusCode, errorCode, cause); 25 | } 26 | 27 | public OAuthException(String message, String path, int httpStatusCode, String errorCode, List details) { 28 | super(message, path, httpStatusCode, errorCode, details); 29 | } 30 | 31 | public OAuthException(String message, 32 | String path, int httpStatusCode, String errorCode, List details, Throwable cause) { 33 | super(message, path, httpStatusCode, errorCode, details, cause); 34 | } 35 | 36 | public OAuthException(String message) { 37 | super(message); 38 | } 39 | 40 | public OAuthException(String message, Throwable cause) { 41 | super(message, cause); 42 | } 43 | 44 | @SuppressWarnings("unused") 45 | public boolean isExpiredToken() { 46 | return (StringUtils.isNotBlank(getErrorCode()) && getErrorCode().equalsIgnoreCase("TOKEN_EXPIRED")); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/adapter/JackSonDateDeserializer.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.adapter; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | 7 | import java.io.IOException; 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | public class JackSonDateDeserializer extends JsonDeserializer { 13 | @Override 14 | public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { 15 | try { 16 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(jsonParser.getText()); 17 | } catch (ParseException e) { 18 | throw new RuntimeException(e); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/adapter/UTCDateDeserializer.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.adapter; 2 | 3 | import com.fasterxml.jackson.core.JsonParser; 4 | import com.fasterxml.jackson.databind.DeserializationContext; 5 | import com.fasterxml.jackson.databind.JsonDeserializer; 6 | 7 | import java.io.IOException; 8 | import java.text.ParseException; 9 | import java.text.SimpleDateFormat; 10 | import java.util.Date; 11 | 12 | public class UTCDateDeserializer extends JsonDeserializer { 13 | 14 | private static final SimpleDateFormat utcFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'"); 15 | 16 | @Override 17 | public Date deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException { 18 | try { 19 | return utcFormat.parse(jsonParser.getText()); 20 | } catch (ParseException e) { 21 | throw new RuntimeException(e); 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/client/BaseOperation.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.client; 2 | 3 | import lombok.RequiredArgsConstructor; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | 7 | @RequiredArgsConstructor 8 | public abstract class BaseOperation { 9 | protected final HttpUrl baseUrl; 10 | protected final String apiToken; 11 | protected final OkHttpClient client; 12 | } 13 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/constant/GrantType.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.constant; 2 | 3 | public class GrantType { 4 | public static final String CLIENT_CREDENTIALS = "client_credentials"; 5 | } 6 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/constant/HttpHeader.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.constant; 2 | 3 | public class HttpHeader { 4 | 5 | public static final String ACCEPT = "Accept"; 6 | public static final String CONTENT_TYPE = "Content-Type"; 7 | public static final String AUTHORIZATION = "Authorization"; 8 | public static final String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization"; 9 | } 10 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/constant/MediaType.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.constant; 2 | 3 | public class MediaType { 4 | public static final String APPLICATION_JSON = "application/json"; 5 | } 6 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/dto/request/BaseRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.dto.request; 2 | 3 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 4 | import software.reloadly.sdk.core.exception.ReloadlyException; 5 | import okhttp3.OkHttpClient; 6 | import okhttp3.Response; 7 | 8 | import java.io.IOException; 9 | 10 | public abstract class BaseRequest implements Request { 11 | 12 | private final OkHttpClient client; 13 | 14 | BaseRequest(OkHttpClient client) { 15 | this.client = client; 16 | } 17 | 18 | protected abstract okhttp3.Request createRequest() throws ReloadlyException; 19 | 20 | protected abstract T parseResponse(Response response) throws ReloadlyException; 21 | 22 | /** 23 | * Executes this request. 24 | * 25 | * @return the response body JSON decoded as T 26 | * @throws ReloadlyException if the request execution fails. 27 | */ 28 | @Override 29 | public T execute() throws ReloadlyException { 30 | okhttp3.Request request = createRequest(); 31 | try (Response response = client.newCall(request).execute()) { 32 | return parseResponse(response); 33 | } catch (ReloadlyException e) { 34 | throw e; 35 | } catch (IOException e) { 36 | throw new ReloadlyException("Failed to execute request", e); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/dto/request/CustomizableRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.dto.request; 2 | 3 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 4 | 5 | @SuppressWarnings("unused") 6 | public interface CustomizableRequest extends Request { 7 | 8 | CustomizableRequest addHeader(String name, String value); 9 | 10 | CustomizableRequest addParameter(String name, Object value); 11 | 12 | CustomizableRequest setBody(Object body); 13 | } 14 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/dto/request/interfaces/Request.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.dto.request.interfaces; 2 | 3 | 4 | import software.reloadly.sdk.core.exception.APIException; 5 | import software.reloadly.sdk.core.exception.ReloadlyException; 6 | 7 | /** 8 | * Class that represents an HTTP Request that can be executed. 9 | * 10 | * @param the type of payload expected in the response after the execution. 11 | */ 12 | public interface Request { 13 | 14 | /** 15 | * Executes this request. 16 | * 17 | * @return the response body JSON decoded as T 18 | * @throws APIException if the request was executed but the response wasn't successful. 19 | * @throws ReloadlyException if the request couldn't be created or executed successfully. 20 | */ 21 | T execute() throws ReloadlyException; 22 | } 23 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/enums/Version.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.enums; 2 | 3 | import lombok.Getter; 4 | import lombok.RequiredArgsConstructor; 5 | 6 | @Getter 7 | @RequiredArgsConstructor 8 | @SuppressWarnings("SpellCheckingInspection") 9 | public enum Version { 10 | 11 | AIRTIME_V1("application/com.reloadly.topups-v1+json"), 12 | GIFTCARD_V1("application/com.reloadly.giftcards-v1+json"), 13 | AUTHENTICATION_V1("application/com.reloadly.authentication-v1+json"); 14 | 15 | private final String value; 16 | } 17 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/filter/BaseFilter.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.filter; 2 | 3 | import lombok.Getter; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | @Getter 9 | public abstract class BaseFilter { 10 | protected final Map parameters = new HashMap<>(); 11 | } 12 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/filter/QueryFilter.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.filter; 2 | 3 | 4 | import software.reloadly.sdk.core.internal.util.Asserter; 5 | 6 | 7 | public class QueryFilter extends BaseFilter { 8 | 9 | /** 10 | * Filter by page 11 | * 12 | * @param pageNumber the page number to retrieve. 13 | * @param pageSize the amount of items per page to retrieve. 14 | * @return this filter instance 15 | */ 16 | public QueryFilter withPage(int pageNumber, int pageSize) { 17 | Asserter.assertNotNull(pageNumber, "Page number"); 18 | Asserter.assertNotNull(pageSize, "Page size"); 19 | 20 | if (pageNumber <= 0) { 21 | throw new IllegalArgumentException("Filter page number must be greater than zero"); 22 | } 23 | 24 | if (pageSize <= 0) { 25 | throw new IllegalArgumentException("Filter page size must be greater than zero"); 26 | } 27 | 28 | parameters.put("page", pageNumber); 29 | parameters.put("size", pageSize); 30 | return this; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/interceptor/TelemetryInterceptor.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.interceptor; 2 | 3 | import lombok.Getter; 4 | import lombok.Setter; 5 | import org.springframework.lang.NonNull; 6 | import software.reloadly.sdk.core.internal.net.Telemetry; 7 | import okhttp3.Interceptor; 8 | import okhttp3.Response; 9 | 10 | import java.io.IOException; 11 | 12 | @Getter 13 | @Setter 14 | public class TelemetryInterceptor implements Interceptor { 15 | 16 | private boolean enabled; 17 | private Telemetry telemetry; 18 | 19 | public TelemetryInterceptor(Telemetry telemetry) { 20 | this.telemetry = telemetry; 21 | this.enabled = true; 22 | } 23 | 24 | @NonNull 25 | @Override 26 | public Response intercept(@NonNull Chain chain) throws IOException { 27 | if (!enabled) { 28 | return chain.proceed(chain.request()); 29 | } 30 | 31 | okhttp3.Request request = chain.request() 32 | .newBuilder() 33 | .addHeader(Telemetry.HEADER_NAME, telemetry.getValue()) 34 | .build(); 35 | return chain.proceed(request); 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/net/ServiceAPI.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.net; 2 | 3 | import com.auth0.jwt.JWT; 4 | import com.auth0.jwt.interfaces.DecodedJWT; 5 | import lombok.AccessLevel; 6 | import lombok.Getter; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.lang.Nullable; 9 | import software.reloadly.sdk.core.exception.ReloadlyException; 10 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 11 | import software.reloadly.sdk.core.internal.util.Asserter; 12 | import software.reloadly.sdk.core.net.HttpOptions; 13 | 14 | import java.util.Date; 15 | import java.util.List; 16 | 17 | import static org.apache.commons.lang3.StringUtils.isBlank; 18 | 19 | @Getter 20 | @SuppressWarnings("unused") 21 | public abstract class ServiceAPI extends API { 22 | 23 | protected String accessToken; 24 | @Getter(AccessLevel.NONE) 25 | protected boolean cacheAccessToken; 26 | 27 | public ServiceAPI(String clientId, String clientSecret, String accessToken, 28 | boolean enableLogging, List redactHeaders, HttpOptions options, 29 | Boolean enableTelemetry, String sdkVersion, @Nullable String apiVersion) { 30 | 31 | super(clientId, clientSecret, enableLogging, redactHeaders, options, enableTelemetry, sdkVersion, apiVersion); 32 | 33 | if (StringUtils.isNotBlank(accessToken)) { 34 | this.accessToken = validateAccessToken(accessToken); 35 | if (StringUtils.isNotBlank(this.accessToken)) { 36 | this.cacheAccessToken = true; 37 | } 38 | } 39 | } 40 | 41 | /** 42 | * Retrieve a new API access token to use on new calls. 43 | * This is useful when the token is about to expire or already has. 44 | * 45 | * @param request - The request to refresh the token for 46 | * @throws ReloadlyException - Error captured when executing a http request to the Reloadly Server 47 | */ 48 | public abstract void refreshAccessToken(Request request) throws ReloadlyException; 49 | 50 | public boolean isAccessTokenCached() { 51 | return cacheAccessToken; 52 | } 53 | 54 | protected void validateCredentials() { 55 | 56 | if (isBlank(accessToken) && isBlank(clientId) && isBlank(clientSecret)) { 57 | throw new IllegalArgumentException( 58 | "Either a valid access token or both client id & client secret must be provided" 59 | ); 60 | } else if (isBlank(accessToken)) { 61 | Asserter.assertNotNull(clientId, "Client id"); 62 | Asserter.assertNotNull(clientSecret, "Client secret"); 63 | } else if (isBlank(clientId) && isBlank(clientSecret)) { 64 | Asserter.assertNotNull(accessToken, "Access token"); 65 | } 66 | } 67 | 68 | /** 69 | * Update the API access token to use on new calls. 70 | * This is useful when the token is about to expire or already has. 71 | * 72 | * @param accessToken the token to authenticate the calls with. 73 | */ 74 | protected void setAccessToken(String accessToken) { 75 | Asserter.assertNotNull(accessToken, "Access token"); 76 | this.accessToken = validateAccessToken(accessToken); 77 | } 78 | 79 | @Nullable 80 | private String validateAccessToken(String accessToken) { 81 | 82 | DecodedJWT decodedToken; 83 | try { 84 | decodedToken = JWT.decode(accessToken); 85 | } catch (Exception e) { 86 | return null; 87 | } 88 | 89 | Date expirationDate = decodedToken.getExpiresAt(); 90 | 91 | Date now = new Date(); 92 | long timeLeftInSeconds = (expirationDate.getTime() - now.getTime()) / 1000; 93 | 94 | //Returns true if comparison is 0 or negative or about to expire in 5 minutes or less 95 | return (expirationDate.compareTo(now) <= 0 || (timeLeftInSeconds <= 300)) ? null : accessToken; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/net/Telemetry.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.net; 2 | 3 | import com.fasterxml.jackson.core.JsonProcessingException; 4 | import com.fasterxml.jackson.databind.ObjectMapper; 5 | import lombok.Getter; 6 | import org.apache.commons.codec.binary.Base64; 7 | import org.apache.commons.lang3.StringUtils; 8 | import org.springframework.lang.Nullable; 9 | import software.reloadly.sdk.core.internal.util.TelemetryUtil; 10 | 11 | import java.util.Collections; 12 | import java.util.HashMap; 13 | import java.util.Map; 14 | 15 | @Getter 16 | @SuppressWarnings("WeakerAccess") 17 | public class Telemetry { 18 | 19 | private static final String ENV_KEY = "env"; 20 | private static final String JAVA_KEY = "java"; 21 | private static final String NAME_KEY = "name"; 22 | private static final String VERSION_KEY = "api-version"; 23 | public static final String HEADER_NAME = "Reloadly-Client"; 24 | private static final String LIBRARY_VERSION_KEY = "reloadly-sdk-java"; 25 | 26 | private final String name; 27 | private final String value; 28 | private final String apiVersion; 29 | private final String libraryVersion; 30 | private final Map env; 31 | 32 | public Telemetry(String name, String libraryVersion) { 33 | this(name, libraryVersion, null); 34 | } 35 | 36 | public Telemetry(String name, String libraryVersion, @Nullable String apiVersion) { 37 | 38 | this.name = name; 39 | this.apiVersion = apiVersion; 40 | this.libraryVersion = libraryVersion; 41 | 42 | if (name == null) { 43 | env = Collections.emptyMap(); 44 | value = null; 45 | return; 46 | } 47 | 48 | Map values = new HashMap<>(); 49 | values.put(NAME_KEY, name); 50 | if (StringUtils.isNotBlank(apiVersion)) { 51 | values.put(VERSION_KEY, apiVersion); 52 | } 53 | 54 | HashMap tmpEnv = new HashMap<>(); 55 | tmpEnv.put(JAVA_KEY, TelemetryUtil.getJDKVersion()); 56 | if (StringUtils.isNotBlank(libraryVersion)) { 57 | tmpEnv.put(LIBRARY_VERSION_KEY, libraryVersion); 58 | } 59 | this.env = Collections.unmodifiableMap(tmpEnv); 60 | values.put(ENV_KEY, env); 61 | 62 | String tmpValue; 63 | try { 64 | String json = new ObjectMapper().writeValueAsString(values); 65 | tmpValue = Base64.encodeBase64URLSafeString(json.getBytes()); 66 | } catch (JsonProcessingException e) { 67 | tmpValue = null; 68 | e.printStackTrace(); 69 | } 70 | value = tmpValue; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/util/Asserter.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.util; 2 | 3 | import okhttp3.HttpUrl; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.apache.commons.validator.routines.EmailValidator; 6 | 7 | import java.util.Collection; 8 | 9 | public abstract class Asserter { 10 | 11 | public static void assertNotNull(Object value, String name) throws IllegalArgumentException { 12 | if (value == null) { 13 | throw new IllegalArgumentException(String.format("'%s' cannot be null!", name)); 14 | } 15 | } 16 | 17 | public static void assertGreaterThanZero(Number value, String name) throws IllegalArgumentException { 18 | if (value.doubleValue() <= 0) { 19 | throw new IllegalArgumentException(String.format("'%s' must be greater than zero!", name)); 20 | } 21 | } 22 | 23 | public static void assertNotBlank(String value, String name) throws IllegalArgumentException { 24 | if (StringUtils.isBlank(value)) { 25 | throw new IllegalArgumentException(String.format("'%s' cannot be null or empty!", name)); 26 | } 27 | } 28 | 29 | public static void assertValidUrl(String value, String name) throws IllegalArgumentException { 30 | if (StringUtils.isBlank(value) || HttpUrl.parse(value) == null) { 31 | throw new IllegalArgumentException(String.format("'%s' must be a valid URL!", name)); 32 | } 33 | } 34 | 35 | public static void assertNotEmpty(Collection value, String name) throws IllegalArgumentException { 36 | if (value == null) { 37 | throw new IllegalArgumentException(String.format("'%s' cannot be null!", name)); 38 | } 39 | if (value.size() == 0) { 40 | throw new IllegalArgumentException(String.format("'%s' cannot be empty!", name)); 41 | } 42 | } 43 | 44 | public static void assertValidEmail(String email, String name) { 45 | assertNotBlank(email, name); 46 | if (!EmailValidator.getInstance().isValid(email)) { 47 | throw new IllegalArgumentException(String.format("'%s' is not a valid email address!", name)); 48 | } 49 | } 50 | 51 | public static void assertValidPhoneNumber(String value, String name) { 52 | assertNotBlank(value, name); 53 | if (!StringUtils.isNumeric(value)) { 54 | String msg = "'%s' must contain '+' signs and numbers only. No other characters allowed!"; 55 | throw new IllegalArgumentException(String.format(msg, name)); 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/util/ExceptionUtil.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.util; 2 | 3 | import software.reloadly.sdk.core.dto.APIError; 4 | import software.reloadly.sdk.core.exception.APIException; 5 | import software.reloadly.sdk.core.exception.oauth.OAuthException; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.Setter; 9 | 10 | import static org.apache.commons.lang3.StringUtils.isNotBlank; 11 | 12 | @Getter 13 | @Setter 14 | @EqualsAndHashCode 15 | public class ExceptionUtil { 16 | 17 | public static APIException convert(APIError apiError, int httpStatusCode) { 18 | return setAdditionalFields(apiError, doGetApiException(apiError, httpStatusCode)); 19 | } 20 | 21 | public static APIException convert(APIError apiError, int httpStatusCode, Throwable cause) { 22 | return setAdditionalFields(apiError, doGetApiException(apiError, httpStatusCode, cause)); 23 | } 24 | 25 | private static APIException doGetApiException(APIError apiError, int httpStatusCode) { 26 | if (isAuthenticationError(apiError)) { 27 | return new OAuthException(apiError.getMessage(), httpStatusCode, apiError.getPath()); 28 | } 29 | return new APIException(apiError.getMessage(), httpStatusCode, apiError.getPath()); 30 | } 31 | 32 | private static APIException doGetApiException(APIError apiError, int httpStatusCode, Throwable cause) { 33 | if (isAuthenticationError(apiError)) { 34 | return new OAuthException(apiError.getMessage(), httpStatusCode, apiError.getPath(), cause); 35 | } 36 | return new APIException(apiError.getMessage(), httpStatusCode, apiError.getPath(), cause); 37 | } 38 | 39 | private static APIException setAdditionalFields(APIError apiError, APIException apiException) { 40 | if (apiError.getErrorCode() != null && !apiError.getErrorCode().trim().isEmpty()) { 41 | apiException.setErrorCode(apiError.getErrorCode()); 42 | } 43 | 44 | if (apiError.getTimeStamp() != null) { 45 | apiException.setTimeStamp(apiError.getTimeStamp()); 46 | } 47 | 48 | if (apiError.getDetails() != null) { 49 | apiException.setDetails(apiError.getDetails()); 50 | } 51 | 52 | return apiException; 53 | } 54 | 55 | private static boolean isAuthenticationError(APIError apiError) { 56 | return isNotBlank(apiError.getPath()) && apiError.getPath().equalsIgnoreCase("/oauth/token"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/internal/util/TelemetryUtil.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.internal.util; 2 | 3 | 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.springframework.lang.Nullable; 6 | import software.reloadly.sdk.core.internal.interceptor.TelemetryInterceptor; 7 | import software.reloadly.sdk.core.internal.net.Telemetry; 8 | 9 | public class TelemetryUtil { 10 | 11 | private static final String JAVA_SPECIFICATION_VERSION = "java.specification.version"; 12 | 13 | public static TelemetryInterceptor getTelemetryInterceptor(String libraryVersion, @Nullable String apiVersion) { 14 | String name = "reloadly-sdk-java"; 15 | Asserter.assertNotBlank(libraryVersion, "Library version"); 16 | if (StringUtils.isNotBlank(apiVersion)) { 17 | return new TelemetryInterceptor(new Telemetry(name, libraryVersion, apiVersion)); 18 | } 19 | return new TelemetryInterceptor(new Telemetry(name, libraryVersion)); 20 | } 21 | 22 | public static String getJDKVersion() { 23 | try { 24 | return System.getProperty(JAVA_SPECIFICATION_VERSION); 25 | } catch (Exception ignored) { 26 | return Runtime.class.getPackage().getSpecificationVersion(); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/net/HttpOptions.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.net; 2 | 3 | import lombok.Builder; 4 | import lombok.Getter; 5 | 6 | import java.time.Duration; 7 | 8 | import static java.time.Duration.ofSeconds; 9 | 10 | /** 11 | * Used to configure additional configuration options when customizing the API client instance. 12 | */ 13 | @Getter 14 | public class HttpOptions { 15 | 16 | private final Duration readTimeout; 17 | private final Duration writeTimeout; 18 | private final Duration connectTimeout; 19 | private final ProxyOptions proxyOptions; 20 | 21 | @Builder 22 | @SuppressWarnings("unused") 23 | public HttpOptions(Duration readTimeout, Duration writeTimeout, Duration connectTimeout, ProxyOptions proxyOptions) { 24 | this.readTimeout = readTimeout; 25 | this.writeTimeout = writeTimeout; 26 | this.connectTimeout = connectTimeout; 27 | this.proxyOptions = proxyOptions; 28 | } 29 | 30 | public HttpOptions() { 31 | proxyOptions = null; 32 | this.readTimeout = ofSeconds(180); 33 | this.writeTimeout = ofSeconds(180); 34 | this.connectTimeout = ofSeconds(180); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /java-sdk-core/src/main/java/software/reloadly/sdk/core/net/ProxyOptions.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.core.net; 2 | 3 | import lombok.Getter; 4 | import okhttp3.Credentials; 5 | import org.apache.commons.lang3.StringUtils; 6 | import software.reloadly.sdk.core.internal.util.Asserter; 7 | 8 | import java.net.Proxy; 9 | 10 | /** 11 | * Used to configure Java Proxy-related configurations. 12 | */ 13 | @Getter 14 | public class ProxyOptions { 15 | 16 | private final Proxy proxy; 17 | private String proxyUsername; 18 | private char[] proxyPassword; 19 | private String basicAuthentication; 20 | 21 | /** 22 | * Builds a new instance using the given Proxy. 23 | * The Proxy will not have authentication unless {@link #basicAuthentication} is set. 24 | * 25 | * @param proxy - The proxy setting 26 | */ 27 | @SuppressWarnings("unused") 28 | public ProxyOptions(Proxy proxy) { 29 | Asserter.assertNotNull(proxy, "proxy"); 30 | this.proxy = proxy; 31 | } 32 | 33 | @SuppressWarnings("unused") 34 | public ProxyOptions(Proxy proxy, String proxyUsername, char[] proxyPassword) { 35 | Asserter.assertNotNull(proxy, "proxy"); 36 | this.proxy = proxy; 37 | this.proxyUsername = proxyUsername; 38 | this.proxyPassword = proxyPassword; 39 | validateAndBuildAuthenticationCredentials(); 40 | } 41 | 42 | /** 43 | * Validate and build the authentication value to use for this Proxy. 44 | */ 45 | private void validateAndBuildAuthenticationCredentials() { 46 | if (StringUtils.isNotBlank(proxyUsername) || proxyPassword != null) { 47 | Asserter.assertNotBlank(proxyUsername, "Proxy username"); 48 | Asserter.assertNotNull(proxyPassword, "Proxy password"); 49 | this.basicAuthentication = Credentials.basic(proxyUsername, new String(proxyPassword)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /java-sdk-giftcard/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | 4 | ### STS ### 5 | .apt_generated 6 | .classpath 7 | .factorypath 8 | .project 9 | .settings 10 | .springBeans 11 | 12 | ### IntelliJ IDEA ### 13 | .idea 14 | *.iws 15 | *.iml 16 | *.ipr 17 | 18 | ### NetBeans ### 19 | nbproject/private/ 20 | build/ 21 | nbbuild/ 22 | dist/ 23 | nbdist/ 24 | .nb-gradle/ 25 | 26 | # OS generated files # 27 | ###################### 28 | .DS_Store 29 | .DS_Store? -------------------------------------------------------------------------------- /java-sdk-giftcard/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | java-sdk 8 | software.reloadly 9 | 1.0.3 10 | 11 | 12 | 4.0.0 13 | java-sdk-giftcard 14 | 1.0.3 15 | Reloadly Java SDK :: Services :: Reloadly Giftcard Service 16 | https://github.com/reloadly/reloadly-sdk-java 17 | 18 | The Reloadly Java SDK for the Giftcard API. The module holds the client classes 19 | that are used for communicating with Reloadly Giftcard Service. 20 | 21 | 22 | 23 | 1.29 24 | 25 | 26 | 27 | 28 | software.reloadly 29 | java-sdk-authentication 30 | false 31 | 32 | 33 | com.neovisionaries 34 | nv-i18n 35 | ${nv-i18n.version} 36 | 37 | 38 | 39 | 40 | 41 | 42 | org.jacoco 43 | jacoco-maven-plugin 44 | 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/request/GiftCardOrderRequest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.request; 2 | 3 | import com.fasterxml.jackson.annotation.JsonProperty; 4 | import com.google.gson.annotations.SerializedName; 5 | import com.neovisionaries.i18n.CountryCode; 6 | import lombok.*; 7 | 8 | import java.io.Serializable; 9 | import java.math.BigDecimal; 10 | 11 | @Getter 12 | @ToString 13 | public class GiftCardOrderRequest implements Serializable { 14 | 15 | private static final long serialVersionUID = -5602589800681629739L; 16 | 17 | private final int quantity; 18 | private final Long productId; 19 | private final String senderName; 20 | private final BigDecimal unitPrice; 21 | private final String recipientEmail; 22 | @JsonProperty("recipientPhoneDetails") 23 | @SerializedName("recipientPhoneDetails") 24 | private final Phone recipientPhone; 25 | private final String customIdentifier; 26 | 27 | @Builder 28 | @SuppressWarnings("unused") 29 | public GiftCardOrderRequest(int quantity, 30 | Long productId, 31 | String senderName, 32 | BigDecimal unitPrice, 33 | String recipientEmail, Phone recipientPhone, String customIdentifier) { 34 | 35 | this.quantity = quantity; 36 | this.productId = productId; 37 | this.senderName = senderName; 38 | this.unitPrice = unitPrice; 39 | this.recipientEmail = recipientEmail; 40 | this.recipientPhone = recipientPhone; 41 | this.customIdentifier = customIdentifier; 42 | } 43 | 44 | @Getter 45 | @ToString 46 | @RequiredArgsConstructor 47 | public static class Phone { 48 | private final String phoneNumber; 49 | private final CountryCode countryCode; 50 | } 51 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardBrand.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.*; 7 | 8 | import java.io.Serializable; 9 | 10 | @Getter 11 | @ToString 12 | @EqualsAndHashCode 13 | @SuppressWarnings("unused") 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class GiftcardBrand implements Serializable { 17 | 18 | private static final long serialVersionUID = 4789588050925687831L; 19 | 20 | @JsonProperty("brandId") 21 | private Long id; 22 | @JsonProperty("brandName") 23 | private String name; 24 | } 25 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardCountry.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.io.Serializable; 10 | 11 | @Getter 12 | @ToString 13 | @EqualsAndHashCode 14 | @SuppressWarnings("unused") 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class GiftcardCountry implements Serializable { 18 | 19 | private static final long serialVersionUID = -5061206329979177191L; 20 | 21 | /** 22 | * ISO 3166-1 alpha-2 Country code. See https://www.iso.org/obp/ui/#search 23 | */ 24 | private String isoName; 25 | 26 | /** 27 | * Full country name 28 | */ 29 | private String name; 30 | 31 | /** 32 | * Url of country flag image 33 | */ 34 | private String flagUrl; 35 | } 36 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardDiscount.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import lombok.ToString; 9 | 10 | import java.io.Serializable; 11 | 12 | @Getter 13 | @ToString 14 | @EqualsAndHashCode 15 | @JsonInclude(JsonInclude.Include.NON_NULL) 16 | @JsonIgnoreProperties(ignoreUnknown = true) 17 | public class GiftcardDiscount implements Serializable { 18 | 19 | private static final long serialVersionUID = 5205777808405666653L; 20 | @JsonProperty("discountPercentage") 21 | private Float percentage; 22 | private GiftcardDiscountProduct product; 23 | 24 | @Getter 25 | @EqualsAndHashCode 26 | @JsonInclude(JsonInclude.Include.NON_NULL) 27 | @JsonIgnoreProperties(ignoreUnknown = true) 28 | public static class GiftcardDiscountProduct implements Serializable { 29 | 30 | private static final long serialVersionUID = -5004222688965304622L; 31 | @JsonProperty("productId") 32 | private Long id; 33 | @JsonProperty("productName") 34 | private String name; 35 | private boolean global; 36 | private String countryCode; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardInfo.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | import lombok.ToString; 8 | 9 | import java.io.Serializable; 10 | 11 | @Getter 12 | @ToString 13 | @EqualsAndHashCode 14 | @JsonInclude(JsonInclude.Include.NON_NULL) 15 | @JsonIgnoreProperties(ignoreUnknown = true) 16 | public class GiftcardInfo implements Serializable { 17 | 18 | private static final long serialVersionUID = -357283902385365398L; 19 | private String pinCode; 20 | private String cardNumber; 21 | } 22 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardProduct.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import lombok.EqualsAndHashCode; 7 | import lombok.Getter; 8 | import software.reloadly.sdk.giftcard.enums.GiftcardDenominationType; 9 | 10 | import java.io.Serializable; 11 | import java.math.BigDecimal; 12 | import java.util.Set; 13 | import java.util.TreeMap; 14 | import java.util.TreeSet; 15 | 16 | @Getter 17 | @EqualsAndHashCode 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | public class GiftcardProduct implements Serializable { 21 | 22 | private static final long serialVersionUID = 4061591738267790767L; 23 | 24 | @JsonProperty("productId") 25 | private Long id; 26 | @JsonProperty("productName") 27 | private String name; 28 | private boolean global; 29 | private Float senderFee; 30 | private Float discountPercentage; 31 | private GiftcardDenominationType denominationType; 32 | private String recipientCurrencyCode; 33 | private BigDecimal minRecipientDenomination; 34 | private BigDecimal maxRecipientDenomination; 35 | private String senderCurrencyCode; 36 | private BigDecimal minSenderDenomination; 37 | private BigDecimal maxSenderDenomination; 38 | private GiftcardBrand brand; 39 | private Set logoUrls; 40 | private GiftcardCountry country; 41 | private TreeSet fixedRecipientDenominations; 42 | private TreeSet fixedSenderDenominations; 43 | private TreeMap fixedRecipientToSenderDenominationsMap; 44 | private GiftcardRedeemInstructionSimplified redeemInstruction; 45 | } 46 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardRedeemInstruction.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | 8 | import java.io.Serializable; 9 | 10 | @Getter 11 | @EqualsAndHashCode 12 | @JsonInclude(JsonInclude.Include.NON_NULL) 13 | @JsonIgnoreProperties(ignoreUnknown = true) 14 | public class GiftcardRedeemInstruction implements Serializable { 15 | 16 | private static final long serialVersionUID = 5205777808405666653L; 17 | private Long brandId; 18 | private String concise; 19 | private String verbose; 20 | private String brandName; 21 | } 22 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardRedeemInstructionSimplified.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import lombok.EqualsAndHashCode; 6 | import lombok.Getter; 7 | 8 | import java.io.Serializable; 9 | 10 | @Getter 11 | @EqualsAndHashCode 12 | @SuppressWarnings("unused") 13 | @JsonInclude(JsonInclude.Include.NON_NULL) 14 | @JsonIgnoreProperties(ignoreUnknown = true) 15 | public class GiftcardRedeemInstructionSimplified implements Serializable { 16 | 17 | 18 | private static final long serialVersionUID = 962678749036587376L; 19 | private String concise; 20 | private String verbose; 21 | } 22 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/dto/response/GiftcardTransaction.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.dto.response; 2 | 3 | import com.fasterxml.jackson.annotation.JsonIgnoreProperties; 4 | import com.fasterxml.jackson.annotation.JsonInclude; 5 | import com.fasterxml.jackson.annotation.JsonProperty; 6 | import com.fasterxml.jackson.databind.annotation.JsonDeserialize; 7 | import lombok.EqualsAndHashCode; 8 | import lombok.Getter; 9 | import software.reloadly.sdk.core.internal.adapter.JackSonDateDeserializer; 10 | import software.reloadly.sdk.giftcard.enums.GiftCardTransactionStatus; 11 | 12 | import java.io.Serializable; 13 | import java.math.BigDecimal; 14 | import java.util.Date; 15 | 16 | @Getter 17 | @EqualsAndHashCode 18 | @JsonInclude(JsonInclude.Include.NON_NULL) 19 | @JsonIgnoreProperties(ignoreUnknown = true) 20 | public class GiftcardTransaction implements Serializable { 21 | 22 | private static final long serialVersionUID = 5205777808405666653L; 23 | @JsonProperty("transactionId") 24 | private Long id; 25 | private Float fee; 26 | private Float smsFee; 27 | private Float discount; 28 | private BigDecimal amount; 29 | private String currencyCode; 30 | private String recipientEmail; 31 | private String recipientPhone; 32 | private String customIdentifier; 33 | private GiftCardTransactionStatus status; 34 | private GiftcardTransactionProduct product; 35 | 36 | @JsonProperty("transactionCreatedTime") 37 | @JsonDeserialize(using = JackSonDateDeserializer.class) 38 | private Date date; 39 | 40 | @Getter 41 | @EqualsAndHashCode 42 | @SuppressWarnings("unused") 43 | @JsonInclude(JsonInclude.Include.NON_NULL) 44 | @JsonIgnoreProperties(ignoreUnknown = true) 45 | public static class GiftcardTransactionProduct implements Serializable { 46 | 47 | private static final long serialVersionUID = 8796617953160384773L; 48 | @JsonProperty("productId") 49 | private Long id; 50 | @JsonProperty("productName") 51 | private String name; 52 | private Integer quantity; 53 | private String countryCode; 54 | private String currencyCode; 55 | private GiftcardBrand brand; 56 | private BigDecimal unitPrice; 57 | private BigDecimal totalPrice; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/enums/GiftCardTransactionStatus.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.enums; 2 | 3 | @SuppressWarnings("unused") 4 | public enum GiftCardTransactionStatus { 5 | PROCESSING, 6 | FAILED, 7 | SUCCESSFUL, 8 | PENDING, 9 | REFUNDED 10 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/enums/GiftcardDenominationType.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.enums; 2 | 3 | public enum GiftcardDenominationType { 4 | RANGE, FIXED, FIXED_AND_RANGE 5 | } 6 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/filter/GiftcardProductFilter.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.filter; 2 | 3 | import com.neovisionaries.i18n.CountryCode; 4 | import lombok.Getter; 5 | import software.reloadly.sdk.core.internal.filter.QueryFilter; 6 | import software.reloadly.sdk.core.internal.util.Asserter; 7 | import software.reloadly.sdk.giftcard.operation.GiftcardProductOperations; 8 | 9 | /** 10 | * Class used to filter the results received when calling the giftcard products endpoint. 11 | * Related to the {@link GiftcardProductOperations}. 12 | */ 13 | @Getter 14 | public class GiftcardProductFilter extends QueryFilter { 15 | 16 | private static final String PRODUCT_NAME = "productName"; 17 | private static final String COUNTRY_CODE = "countryCode"; 18 | private static final String INCLUDE_RANGE = "includeRange"; 19 | private static final String INCLUDE_FIXED = "includeFixed"; 20 | private static final String INCLUDE_SIMPLIFY = "simplified"; 21 | 22 | public GiftcardProductFilter() { 23 | parameters.put(INCLUDE_RANGE, true); 24 | parameters.put(INCLUDE_FIXED, true); 25 | parameters.put(INCLUDE_SIMPLIFY, false); 26 | } 27 | 28 | @Override 29 | public GiftcardProductFilter withPage(int pageNumber, int pageSize) { 30 | super.withPage(pageNumber, pageSize); 31 | return this; 32 | } 33 | 34 | public GiftcardProductFilter productName(String productName) { 35 | Asserter.assertNotBlank(productName, "Product name"); 36 | parameters.put(PRODUCT_NAME, productName); 37 | return this; 38 | } 39 | 40 | public GiftcardProductFilter countryCode(CountryCode countryCode) { 41 | Asserter.assertNotNull(countryCode, "Country code"); 42 | parameters.put(COUNTRY_CODE, countryCode.getAlpha2()); 43 | return this; 44 | } 45 | 46 | @SuppressWarnings("unused") 47 | public GiftcardProductFilter simplified(boolean simplified) { 48 | parameters.put(INCLUDE_SIMPLIFY, simplified); 49 | return this; 50 | } 51 | 52 | public GiftcardProductFilter includeRange(boolean includeRange) { 53 | parameters.put(INCLUDE_RANGE, includeRange); 54 | return this; 55 | } 56 | 57 | public GiftcardProductFilter includeFixed(boolean includeFixed) { 58 | parameters.put(INCLUDE_FIXED, includeFixed); 59 | return this; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/operation/BaseGiftcardOperation.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | import software.reloadly.sdk.core.internal.client.BaseOperation; 7 | import software.reloadly.sdk.core.internal.constant.HttpHeader; 8 | import software.reloadly.sdk.core.internal.constant.MediaType; 9 | import software.reloadly.sdk.core.internal.dto.request.CustomRequest; 10 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 11 | import software.reloadly.sdk.core.internal.enums.Version; 12 | import software.reloadly.sdk.core.internal.filter.QueryFilter; 13 | 14 | public class BaseGiftcardOperation extends BaseOperation { 15 | 16 | BaseGiftcardOperation(HttpUrl baseUrl, String apiToken, OkHttpClient client) { 17 | super(baseUrl, apiToken, client); 18 | } 19 | 20 | HttpUrl.Builder buildFilters(QueryFilter filter, String endPoint) { 21 | HttpUrl.Builder builder = getBuilder(endPoint); 22 | 23 | if (filter != null) { 24 | filter.getParameters().forEach((key, value) -> builder.addQueryParameter(key, String.valueOf(value))); 25 | } 26 | 27 | return builder; 28 | } 29 | 30 | protected Request createGetRequest(String url, TypeReference type) { 31 | CustomRequest request = new CustomRequest<>(client, url, "GET", type); 32 | request.addHeader(HttpHeader.ACCEPT, Version.GIFTCARD_V1.getValue()); 33 | request.addHeader(HttpHeader.AUTHORIZATION, "Bearer " + apiToken); 34 | return request; 35 | } 36 | 37 | protected Request createPostRequest(String url, Object body, TypeReference type) { 38 | return new CustomRequest<>(client, url, "POST", type) 39 | .addHeader(HttpHeader.ACCEPT, Version.GIFTCARD_V1.getValue()) 40 | .addHeader(HttpHeader.CONTENT_TYPE, MediaType.APPLICATION_JSON) 41 | .addHeader(HttpHeader.AUTHORIZATION, "Bearer " + apiToken) 42 | .setBody(body); 43 | } 44 | 45 | protected HttpUrl.Builder getBuilder(String endPoint) { 46 | return baseUrl.newBuilder().addPathSegments(endPoint); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/operation/GiftcardDiscountsOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | import software.reloadly.sdk.core.dto.response.Page; 7 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 8 | import software.reloadly.sdk.core.internal.util.Asserter; 9 | import software.reloadly.sdk.giftcard.dto.response.GiftcardDiscount; 10 | 11 | public class GiftcardDiscountsOperations extends BaseGiftcardOperation { 12 | 13 | private static final String END_POINT = "discounts"; 14 | private static final String PATH_SEGMENT_PRODUCTS = "products"; 15 | 16 | public GiftcardDiscountsOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 17 | super(baseUrl, apiToken, client); 18 | } 19 | 20 | public Request> list() { 21 | return createGetRequest(getBuilder(END_POINT).build().toString(), 22 | new TypeReference>() { 23 | } 24 | ); 25 | } 26 | 27 | public Request getByProductId(Long productId) { 28 | Asserter.assertNotNull(productId, "Product id"); 29 | Asserter.assertGreaterThanZero(productId, "Product id"); 30 | String endPoint = PATH_SEGMENT_PRODUCTS + "/" + productId + "/" + END_POINT; 31 | return createGetRequest(getBuilder(endPoint).build().toString(), 32 | new TypeReference() { 33 | } 34 | ); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/operation/GiftcardOrdersOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | import org.apache.commons.lang3.StringUtils; 7 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 8 | import software.reloadly.sdk.core.internal.util.Asserter; 9 | import software.reloadly.sdk.giftcard.dto.request.GiftCardOrderRequest; 10 | import software.reloadly.sdk.giftcard.dto.response.GiftcardInfo; 11 | import software.reloadly.sdk.giftcard.dto.response.GiftcardTransaction; 12 | 13 | import java.util.List; 14 | 15 | public class GiftcardOrdersOperations extends BaseGiftcardOperation { 16 | 17 | private static final String END_POINT = "orders"; 18 | private static final String ORDERS_REDEEM_PATH_SEGMENT = "orders/transactions"; 19 | 20 | public GiftcardOrdersOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 21 | super(baseUrl, apiToken, client); 22 | } 23 | 24 | public Request placeOrder(GiftCardOrderRequest request) { 25 | validateRequest(request); 26 | return createPostRequest(getBuilder(END_POINT).build().toString(), request, 27 | new TypeReference() { 28 | } 29 | ); 30 | } 31 | 32 | public Request> redeem(Long transactionId) { 33 | Asserter.assertNotNull(transactionId, "Transaction id"); 34 | Asserter.assertGreaterThanZero(transactionId, "Transaction id"); 35 | String endPoint = ORDERS_REDEEM_PATH_SEGMENT + "/" + transactionId + "/cards"; 36 | return createGetRequest(getBuilder(endPoint).build().toString(), new TypeReference>() { 37 | }); 38 | } 39 | 40 | private void validateRequest(GiftCardOrderRequest request) { 41 | 42 | Asserter.assertNotNull(request.getQuantity(), "Quantity"); 43 | Asserter.assertGreaterThanZero(request.getQuantity(), "Quantity"); 44 | Asserter.assertNotNull(request.getProductId(), "Product id"); 45 | Asserter.assertGreaterThanZero(request.getProductId(), "Product id"); 46 | Asserter.assertNotBlank(request.getSenderName(), "Sender name"); 47 | Asserter.assertNotNull(request.getUnitPrice(), "Unit price"); 48 | Asserter.assertGreaterThanZero(request.getUnitPrice(), "Unit price"); 49 | 50 | if (request.getRecipientPhone() == null && StringUtils.isBlank(request.getRecipientEmail())) { 51 | throw new IllegalArgumentException("Either recipient email or recipient phone is required"); 52 | } 53 | 54 | if (request.getRecipientPhone() != null) { 55 | Asserter.assertNotBlank(request.getRecipientPhone().getPhoneNumber(), "Recipient phone number"); 56 | Asserter.assertNotNull(request.getRecipientPhone().getCountryCode(), "Recipient phone country code"); 57 | 58 | String phoneNumber = request.getRecipientPhone().getPhoneNumber().replace("+", ""); 59 | Asserter.assertValidPhoneNumber(phoneNumber, "Recipient phone number"); 60 | } 61 | 62 | if (StringUtils.isNotBlank(request.getRecipientEmail())) { 63 | Asserter.assertNotBlank(request.getRecipientEmail(), "Recipient email"); 64 | Asserter.assertValidEmail(request.getRecipientEmail(), "Recipient email"); 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/operation/GiftcardProductOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import com.neovisionaries.i18n.CountryCode; 5 | import okhttp3.HttpUrl; 6 | import okhttp3.OkHttpClient; 7 | import software.reloadly.sdk.core.dto.response.Page; 8 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 9 | import software.reloadly.sdk.core.internal.util.Asserter; 10 | import software.reloadly.sdk.giftcard.dto.response.GiftcardProduct; 11 | import software.reloadly.sdk.giftcard.filter.GiftcardProductFilter; 12 | 13 | import java.util.List; 14 | 15 | public class GiftcardProductOperations extends BaseGiftcardOperation { 16 | 17 | private static final String END_POINT = "products"; 18 | private static final String PATH_SEGMENT_COUNTRIES = "countries"; 19 | 20 | public GiftcardProductOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 21 | super(baseUrl, apiToken, client); 22 | } 23 | 24 | public Request> list() { 25 | return createGetRequest(getBuilder(END_POINT).build().toString(), 26 | new TypeReference>() { 27 | } 28 | ); 29 | } 30 | 31 | public Request> list(GiftcardProductFilter filter) { 32 | return createGetRequest(buildFilters(filter, END_POINT).build().toString(), 33 | new TypeReference>() { 34 | } 35 | ); 36 | } 37 | 38 | public Request> listByCountryCode(CountryCode countryCode) { 39 | Asserter.assertNotNull(countryCode, "Country code"); 40 | HttpUrl.Builder builder = getBuilder(PATH_SEGMENT_COUNTRIES) 41 | .addPathSegment(countryCode.getAlpha2()).addPathSegment(END_POINT); 42 | return createGetRequest(builder.toString(), new TypeReference>() { 43 | }); 44 | } 45 | 46 | public Request> listByCountryCode(CountryCode countryCode, GiftcardProductFilter filter) { 47 | Asserter.assertNotNull(countryCode, "Country code"); 48 | Asserter.assertNotBlank(countryCode.getAlpha2(), "Country code"); 49 | HttpUrl.Builder builder = buildFilters(filter, PATH_SEGMENT_COUNTRIES) 50 | .addPathSegment(countryCode.getAlpha2()).addPathSegment(END_POINT); 51 | return createGetRequest(builder.toString(), new TypeReference>() { 52 | }); 53 | } 54 | 55 | public Request getById(Long productId) { 56 | validateProductId(productId); 57 | HttpUrl.Builder builder = getBuilder(END_POINT).addPathSegment(productId.toString()); 58 | return createGetRequest(builder.toString(), new TypeReference() { 59 | }); 60 | } 61 | 62 | private void validateProductId(Long productId) { 63 | Asserter.assertNotNull(productId, "Product id"); 64 | Asserter.assertGreaterThanZero(productId, "Product id"); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/operation/GiftcardRedeemInstructionsOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 7 | import software.reloadly.sdk.core.internal.util.Asserter; 8 | import software.reloadly.sdk.giftcard.dto.response.GiftcardRedeemInstruction; 9 | 10 | import java.util.List; 11 | 12 | public class GiftcardRedeemInstructionsOperations extends BaseGiftcardOperation { 13 | 14 | private static final String PATH_SEGMENT_BRANDS = "brands"; 15 | private static final String END_POINT = "redeem-instructions"; 16 | 17 | public GiftcardRedeemInstructionsOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 18 | super(baseUrl, apiToken, client); 19 | } 20 | 21 | public Request> list() { 22 | return createGetRequest(getBuilder(END_POINT).build().toString(), 23 | new TypeReference>() { 24 | } 25 | ); 26 | } 27 | 28 | public Request getByBrandId(Long brandId) { 29 | Asserter.assertNotNull(brandId, "Brand id"); 30 | Asserter.assertGreaterThanZero(brandId, "Brand id"); 31 | String endPoint = PATH_SEGMENT_BRANDS + "/" + brandId + "/" + END_POINT; 32 | return createGetRequest(getBuilder(endPoint).build().toString(), 33 | new TypeReference() { 34 | } 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/main/java/software/reloadly/sdk/giftcard/operation/GiftcardTransactionsOperations.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation; 2 | 3 | import com.fasterxml.jackson.core.type.TypeReference; 4 | import okhttp3.HttpUrl; 5 | import okhttp3.OkHttpClient; 6 | import software.reloadly.sdk.core.dto.response.Page; 7 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 8 | import software.reloadly.sdk.core.internal.util.Asserter; 9 | import software.reloadly.sdk.giftcard.dto.response.GiftcardTransaction; 10 | import software.reloadly.sdk.giftcard.filter.GiftcardTransactionFilter; 11 | 12 | public class GiftcardTransactionsOperations extends BaseGiftcardOperation { 13 | 14 | private static final String END_POINT = "reports/transactions"; 15 | 16 | public GiftcardTransactionsOperations(OkHttpClient client, HttpUrl baseUrl, String apiToken) { 17 | super(baseUrl, apiToken, client); 18 | } 19 | 20 | public Request> list() { 21 | return createGetRequest(getBuilder(END_POINT).build().toString(), 22 | new TypeReference>() { 23 | } 24 | ); 25 | } 26 | 27 | public Request> list(GiftcardTransactionFilter filter) { 28 | return createGetRequest(buildFilters(filter, END_POINT).build().toString(), 29 | new TypeReference>() { 30 | } 31 | ); 32 | } 33 | 34 | public Request getById(Long transactionId) { 35 | Asserter.assertNotNull(transactionId, "Transaction id"); 36 | Asserter.assertGreaterThanZero(transactionId, "Transaction id"); 37 | String endPoint = END_POINT + "/" + transactionId; 38 | return createGetRequest(getBuilder(endPoint).build().toString(), 39 | new TypeReference() { 40 | } 41 | ); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/java/software/reloadly/sdk/giftcard/interfaces/IntegrationTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.interfaces; 2 | 3 | import org.junit.jupiter.api.Tag; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Test 12 | @Tag("integration") 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface IntegrationTest { 16 | } 17 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/java/software/reloadly/sdk/giftcard/interfaces/IntegrationTestWithProxy.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.interfaces; 2 | 3 | import org.junit.jupiter.api.Tag; 4 | import org.junit.jupiter.api.Test; 5 | 6 | import java.lang.annotation.ElementType; 7 | import java.lang.annotation.Retention; 8 | import java.lang.annotation.RetentionPolicy; 9 | import java.lang.annotation.Target; 10 | 11 | @Test 12 | @Tag("integration-with-proxy") 13 | @Target(ElementType.METHOD) 14 | @Retention(RetentionPolicy.RUNTIME) 15 | public @interface IntegrationTestWithProxy { 16 | } 17 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/java/software/reloadly/sdk/giftcard/operation/integration/BaseIntegrationTest.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation.integration; 2 | 3 | import org.junit.jupiter.api.BeforeAll; 4 | import software.reloadly.sdk.authentication.client.AuthenticationAPI; 5 | import software.reloadly.sdk.core.enums.Service; 6 | import software.reloadly.sdk.core.exception.ReloadlyException; 7 | 8 | public class BaseIntegrationTest { 9 | 10 | protected static String accessToken; 11 | protected static String sandboxAccessToken; 12 | 13 | @BeforeAll 14 | static void beforeAll() throws ReloadlyException { 15 | String clientId = System.getenv("LIVE_CLIENT_ID"); 16 | String clientSecret = System.getenv("LIVE_CLIENT_SECRET"); 17 | String sandboxClientId = System.getenv("SANDBOX_CLIENT_ID"); 18 | String sandboxClientSecret = System.getenv("SANDBOX_CLIENT_SECRET"); 19 | 20 | accessToken = AuthenticationAPI.builder().clientId(clientId) 21 | .clientSecret(clientSecret) 22 | .service(Service.GIFTCARD) 23 | .build().clientCredentials().getAccessToken().execute().getToken(); 24 | 25 | sandboxAccessToken = AuthenticationAPI.builder().clientId(sandboxClientId) 26 | .clientSecret(sandboxClientSecret) 27 | .service(Service.GIFTCARD_SANDBOX) 28 | .build().clientCredentials().getAccessToken().execute().getToken(); 29 | } 30 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/java/software/reloadly/sdk/giftcard/operation/integration/GiftcardRedeemInstructionsOperationsTests.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.operation.integration; 2 | 3 | import software.reloadly.sdk.core.enums.Environment; 4 | import software.reloadly.sdk.core.internal.dto.request.interfaces.Request; 5 | import software.reloadly.sdk.giftcard.client.GiftcardAPI; 6 | import software.reloadly.sdk.giftcard.dto.response.GiftcardRedeemInstruction; 7 | import software.reloadly.sdk.giftcard.interfaces.IntegrationTest; 8 | 9 | import java.lang.reflect.Field; 10 | import java.util.Arrays; 11 | import java.util.List; 12 | import java.util.stream.Collectors; 13 | 14 | import static org.hamcrest.MatcherAssert.assertThat; 15 | import static org.hamcrest.Matchers.*; 16 | 17 | public class GiftcardRedeemInstructionsOperationsTests extends BaseIntegrationTest { 18 | 19 | @IntegrationTest 20 | public void testListGiftcardRedeemInstructions() throws Exception { 21 | 22 | GiftcardAPI giftcardAPI = GiftcardAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 23 | Request> request = giftcardAPI.redeemInstructions().list(); 24 | assertThat(request, is(notNullValue())); 25 | List redeemInstructions = request.execute(); 26 | redeemInstructions.forEach(this::assertIsValidGiftcardRedeemInstruction); 27 | } 28 | 29 | @IntegrationTest 30 | public void testListGiftcardRedeemInstructionByBrandId() throws Exception { 31 | 32 | long brandId = 25L; 33 | GiftcardAPI giftcardAPI = GiftcardAPI.builder().environment(Environment.LIVE).accessToken(accessToken).build(); 34 | Request request = giftcardAPI.redeemInstructions().getByBrandId(brandId); 35 | assertThat(request, is(notNullValue())); 36 | GiftcardRedeemInstruction redeemInstruction = request.execute(); 37 | assertIsValidGiftcardRedeemInstruction(redeemInstruction); 38 | } 39 | 40 | private void assertIsValidGiftcardRedeemInstruction(GiftcardRedeemInstruction redeemInstruction) { 41 | 42 | int expectedFieldsCount = 4; 43 | List redeemInstructionFields = Arrays.asList("brandId", "brandName", "concise", "verbose"); 44 | 45 | List fields = Arrays.stream(redeemInstruction.getClass().getDeclaredFields()) 46 | .filter(f -> (!f.getName().equalsIgnoreCase("serialVersionUID") && 47 | !f.getName().equalsIgnoreCase("$jacocoData") && 48 | !f.getName().equalsIgnoreCase("__$lineHits$__")) 49 | ).map(Field::getName).collect(Collectors.toList()); 50 | 51 | int actualFieldsCount = fields.size(); 52 | String errorMsg = "Failed asserting that GiftcardRedeemInstruction::class contains " + expectedFieldsCount; 53 | errorMsg += " fields. It actually contains " + actualFieldsCount + " fields"; 54 | assertThat(errorMsg, expectedFieldsCount == actualFieldsCount); 55 | assertThat(redeemInstruction, is(notNullValue())); 56 | redeemInstructionFields.forEach(field -> assertThat(redeemInstruction, hasProperty(field))); 57 | assertThat(redeemInstruction.getBrandId(), is(notNullValue())); 58 | assertThat(redeemInstruction.getConcise(), is(not(emptyOrNullString()))); 59 | assertThat(redeemInstruction.getVerbose(), is(not(emptyOrNullString()))); 60 | assertThat(redeemInstruction.getBrandName(), is(not(emptyOrNullString()))); 61 | assertThat(redeemInstruction.getBrandId(), is(greaterThanOrEqualTo(0L))); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/java/software/reloadly/sdk/giftcard/util/GiftcardAPIMockServer.java: -------------------------------------------------------------------------------- 1 | package software.reloadly.sdk.giftcard.util; 2 | 3 | import okhttp3.mockwebserver.MockResponse; 4 | import okhttp3.mockwebserver.MockWebServer; 5 | import okhttp3.mockwebserver.RecordedRequest; 6 | import software.reloadly.sdk.core.internal.constant.HttpHeader; 7 | import software.reloadly.sdk.core.internal.enums.Version; 8 | 9 | import java.io.IOException; 10 | import java.nio.file.Files; 11 | import java.nio.file.Paths; 12 | 13 | public class GiftcardAPIMockServer { 14 | 15 | public static final String ACCESS_TOKEN = "eyJhbGciOiJIUzI1NiJ9.eyJJc3N1ZXIiOiJyZWxvYWRseSIsImV4cCI6MTY3NTY0N" + 16 | "DE5OTgsImlhdCI6MTYwOTEzOTU5OH0.fKuCwsOC7d6oDEG2hZdQSwrtwQtxQUx9ueRXlt_9mtA"; 17 | 18 | private final MockWebServer server; 19 | 20 | public GiftcardAPIMockServer() throws Exception { 21 | server = new MockWebServer(); 22 | server.start(); 23 | } 24 | 25 | public void stop() throws IOException { 26 | server.shutdown(); 27 | } 28 | 29 | public String getBaseUrl() { 30 | return server.url("").toString().replaceAll("/\\z", ""); 31 | } 32 | 33 | public RecordedRequest takeRequest() throws InterruptedException { 34 | return server.takeRequest(); 35 | } 36 | 37 | private String readTextFile(String path) throws IOException { 38 | return new String(Files.readAllBytes(Paths.get(path))); 39 | } 40 | 41 | public void jsonResponse(String path, int statusCode) throws IOException { 42 | MockResponse response = new MockResponse().setResponseCode(statusCode) 43 | .addHeader(HttpHeader.CONTENT_TYPE, Version.GIFTCARD_V1.getValue()) 44 | .setBody(readTextFile(path)); 45 | 46 | server.enqueue(response); 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/discount/discount_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "product": { 3 | "productId": 100, 4 | "productName": "CALL OF DUTY: MODERN WARFARE® STANDARD EDITION", 5 | "countryCode": "MG", 6 | "global": true 7 | }, 8 | "discountPercentage": 5.95 9 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/order/transaction_redeem_response.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "cardNumber": "EWWVWtest", 4 | "pinCode": "16163test" 5 | } 6 | ] -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/order/transaction_with_recipient_email_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 815, 3 | "amount": 17.52, 4 | "discount": 0.49, 5 | "currencyCode": "USD", 6 | "fee": 0.50, 7 | "recipientEmail": "empi2b@gmail.com", 8 | "customIdentifier": "348938934t3343434", 9 | "status": "SUCCESSFUL", 10 | "product": { 11 | "productId": 10, 12 | "productName": "App Store & iTunes Austria", 13 | "countryCode": "AU", 14 | "quantity": 1, 15 | "unitPrice": 15.00, 16 | "totalPrice": 15.00, 17 | "currencyCode": "EUR", 18 | "brand": { 19 | "brandId": 3, 20 | "brandName": "App Store & iTunes" 21 | } 22 | }, 23 | "smsFee": 0.00, 24 | "recipientPhone": null, 25 | "transactionCreatedTime": "2022-01-03 23:30:41" 26 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/order/transaction_with_recipient_phone_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 820, 3 | "amount": 17.45, 4 | "discount": 0.49, 5 | "currencyCode": "USD", 6 | "fee": 0.50, 7 | "recipientEmail": null, 8 | "customIdentifier": "34344567hy6566776", 9 | "status": "SUCCESSFUL", 10 | "product": { 11 | "productId": 10, 12 | "productName": "App Store & iTunes Austria", 13 | "countryCode": "AU", 14 | "quantity": 1, 15 | "unitPrice": 15.00, 16 | "totalPrice": 15.00, 17 | "currencyCode": "EUR", 18 | "brand": { 19 | "brandId": 3, 20 | "brandName": "App Store & iTunes" 21 | } 22 | }, 23 | "smsFee": 0.01, 24 | "recipientPhone": "13058989796", 25 | "transactionCreatedTime": "2022-01-04 00:25:20" 26 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/product/product_by_id_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "productId": 10, 3 | "productName": "App Store & iTunes Austria", 4 | "global": false, 5 | "senderFee": 0.5, 6 | "discountPercentage": 2.9, 7 | "denominationType": "FIXED", 8 | "recipientCurrencyCode": "EUR", 9 | "minRecipientDenomination": null, 10 | "maxRecipientDenomination": null, 11 | "senderCurrencyCode": "USD", 12 | "minSenderDenomination": null, 13 | "maxSenderDenomination": null, 14 | "fixedRecipientDenominations": [ 15 | 5.00, 16 | 10.00, 17 | 15.00, 18 | 25.00, 19 | 50.00, 20 | 100.00 21 | ], 22 | "fixedSenderDenominations": [ 23 | 5.65, 24 | 11.30, 25 | 16.95, 26 | 28.25, 27 | 56.50, 28 | 113.00 29 | ], 30 | "fixedRecipientToSenderDenominationsMap": { 31 | "5.00": 5.65, 32 | "10.00": 11.30, 33 | "15.00": 16.95, 34 | "25.00": 28.25, 35 | "50.00": 56.50, 36 | "100.00": 113.00 37 | }, 38 | "logoUrls": [ 39 | "https://cdn.reloadly.com/giftcards/efff4800-085b-463e-818b-64d22c599f8a.jpg" 40 | ], 41 | "brand": { 42 | "brandId": 3, 43 | "brandName": "App Store & iTunes" 44 | }, 45 | "country": { 46 | "isoName": "AU", 47 | "name": "Austria", 48 | "flagUrl": "https://s3.amazonaws.com/rld-flags/kr.svg" 49 | }, 50 | "redeemInstruction": { 51 | "concise": "Go to apple.com/redeem to add to your Apple Account balance.", 52 | "verbose": "Use it for purchases at any Apple Store location, on the Apple Store-app, apple.com, the App Store, iTunes, Apple Music, Apple TV, Apple News, Apple Books, Apple Arcade, iCloud, and other Apple properties. Use the Apple Gift Card for App Store, iTunes, iPhone, iPad, Air-pods, Mac book, accessories, and more. No returns or refunds on Apple Gift Cards. Terms apply. The Apple Gift Card can be used two ways: For online purchases, go to apple.com/redeem to add to your Apple Account balance. Bring this email to any Apple Store location. Beware of gift card scams. Do not share your code. Terms & Conditions Valid only for U.S. transactions in Apple properties. For assistance, visit support.apple.com/giftcard or call 1-800-275-2273. Not redeemable at Apple resellers or for cash, and no resale, refunds, or exchanges, except as required by law. Apple is not responsible for unauthorized use. Terms apply" 53 | } 54 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/redeem_instructions/redeem_instructions_by_brand_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "brandId": 25, 3 | "brandName": "Google play", 4 | "concise": "To redeem, enter code in the Play Store app or https://play.google.com/store", 5 | "verbose": "Use this gift card's code on Google Play. Any other request for the code may bea scam. To redeem, enter code in the Play Store app or https://play.google.com/store See play.google.com/us-card-terms for full terms. Usable for purchases of eligible items on Google Play only. Not usable for hardware and certain subscriptions. Other limits may apply. No fees or expiration dates.Except as required by law, card is not redeemable for cash or other cards; not reloadable or refundable; cannot be combined with other non-Google Play balances in your Google Payments account, resold, exchanged or transferred for value." 6 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/transaction/transaction_by_id_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "transactionId": 813, 3 | "amount": 17.52, 4 | "discount": 0.49, 5 | "currencyCode": "USD", 6 | "fee": 0.50, 7 | "recipientEmail": "empi2b@gmail.com", 8 | "customIdentifier": null, 9 | "status": "SUCCESSFUL", 10 | "product": { 11 | "productId": 10, 12 | "productName": "App Store & iTunes Austria", 13 | "countryCode": "AU", 14 | "quantity": 1, 15 | "unitPrice": 15.00, 16 | "totalPrice": 15.00, 17 | "currencyCode": "EUR", 18 | "brand": { 19 | "brandId": 3, 20 | "brandName": "App Store & iTunes" 21 | } 22 | }, 23 | "smsFee": 0.00, 24 | "recipientPhone": null, 25 | "transactionCreatedTime": "2022-01-03 22:25:39" 26 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/transaction/transactions_paged_filtered_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "transactionId": 814, 5 | "amount": 57.24, 6 | "discount": 1.65, 7 | "currencyCode": "USD", 8 | "fee": 0.50, 9 | "recipientEmail": "empi2b@gmail.com", 10 | "customIdentifier": null, 11 | "status": "SUCCESSFUL", 12 | "product": { 13 | "productId": 14, 14 | "productName": "App Store & iTunes DE", 15 | "countryCode": "DE", 16 | "quantity": 1, 17 | "unitPrice": 50.00, 18 | "totalPrice": 50.00, 19 | "currencyCode": "EUR", 20 | "brand": { 21 | "brandId": 3, 22 | "brandName": "App Store & iTunes" 23 | } 24 | }, 25 | "smsFee": 0.00, 26 | "recipientPhone": null, 27 | "transactionCreatedTime": "2022-01-03 22:25:57" 28 | }, 29 | { 30 | "transactionId": 813, 31 | "amount": 17.52, 32 | "discount": 0.49, 33 | "currencyCode": "USD", 34 | "fee": 0.50, 35 | "recipientEmail": "empi2b@gmail.com", 36 | "customIdentifier": null, 37 | "status": "SUCCESSFUL", 38 | "product": { 39 | "productId": 10, 40 | "productName": "App Store & iTunes Austria", 41 | "countryCode": "AU", 42 | "quantity": 1, 43 | "unitPrice": 15.00, 44 | "totalPrice": 15.00, 45 | "currencyCode": "EUR", 46 | "brand": { 47 | "brandId": 3, 48 | "brandName": "App Store & iTunes" 49 | } 50 | }, 51 | "smsFee": 0.00, 52 | "recipientPhone": null, 53 | "transactionCreatedTime": "2022-01-03 22:25:39" 54 | } 55 | ], 56 | "pageable": { 57 | "sort": { 58 | "sorted": false, 59 | "unsorted": true, 60 | "empty": true 61 | }, 62 | "pageNumber": 0, 63 | "pageSize": 50, 64 | "offset": 0, 65 | "unpaged": false, 66 | "paged": true 67 | }, 68 | "totalElements": 2, 69 | "totalPages": 1, 70 | "last": true, 71 | "sort": { 72 | "sorted": false, 73 | "unsorted": true, 74 | "empty": true 75 | }, 76 | "numberOfElements": 2, 77 | "first": true, 78 | "size": 50, 79 | "number": 0, 80 | "empty": false 81 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/src/test/resources/transaction/transactions_paged_unfiltered_response.json: -------------------------------------------------------------------------------- 1 | { 2 | "content": [ 3 | { 4 | "transactionId": 812, 5 | "amount": 25.50, 6 | "discount": 1.88, 7 | "currencyCode": "USD", 8 | "fee": 0.50, 9 | "recipientEmail": "empi2b@gmail.com", 10 | "customIdentifier": null, 11 | "status": "SUCCESSFUL", 12 | "product": { 13 | "productId": 1, 14 | "productName": "1-800-PetSupplies", 15 | "countryCode": "US", 16 | "quantity": 1, 17 | "unitPrice": 25.00, 18 | "totalPrice": 25.00, 19 | "currencyCode": "USD", 20 | "brand": { 21 | "brandId": 1, 22 | "brandName": "1-800-PetSupplies" 23 | } 24 | }, 25 | "smsFee": 0.00, 26 | "recipientPhone": null, 27 | "transactionCreatedTime": "2022-01-03 20:28:55" 28 | } 29 | ], 30 | "pageable": { 31 | "sort": { 32 | "sorted": false, 33 | "unsorted": true, 34 | "empty": true 35 | }, 36 | "pageNumber": 0, 37 | "pageSize": 50, 38 | "offset": 0, 39 | "unpaged": false, 40 | "paged": true 41 | }, 42 | "totalElements": 1, 43 | "totalPages": 1, 44 | "last": true, 45 | "sort": { 46 | "sorted": false, 47 | "unsorted": true, 48 | "empty": true 49 | }, 50 | "numberOfElements": 1, 51 | "first": true, 52 | "size": 50, 53 | "number": 0, 54 | "empty": false 55 | } -------------------------------------------------------------------------------- /java-sdk-giftcard/usage/GIFTCARD-DISCOUNT-OPERATIONS.md: -------------------------------------------------------------------------------- 1 | # Giftcard Discounts Operations 2 | 3 | Discounts or commissions are a way for you to check what percentage discount rate you will get for each **Giftcard 4 | Product** when you make a successful order. These operations can be used to calculate your profits. All Commissions are 5 | paid instantly when an order is processed. 6 | 7 | Thing to note on the response are the fields **global**, which indicate if the gift card can be used globally across 8 | different countries, and **percentage**, which indicates the percentage discount available for every purchase of the 9 | gift card. 10 | 11 | ## Giftcard Discounts - List 12 | 13 | ```java 14 | GiftcardAPI giftcardAPI = GiftcardAPI.builder() 15 | .clientId(clientId) 16 | .clientSecret(clientSecret) 17 | .environment(Environment.SANDBOX) 18 | .build(); 19 | 20 | Request> request; 21 | try { 22 | request = giftcardAPI.discounts().list(); 23 | } catch (ReloadlyException e) { 24 | // api error retrieving access_token 25 | } 26 | 27 | Page discountPage = null; 28 | try { 29 | discountPage = request.execute(); 30 | } catch (APIException e) { 31 | // api error 32 | } catch (ReloadlyException e) { 33 | // request error 34 | } catch (Exception e) { 35 | // all others 36 | } 37 | ``` 38 | 39 | ## Giftcard Discounts - Get by product id 40 | 41 | ```java 42 | GiftcardAPI giftcardAPI = GiftcardAPI.builder() 43 | .clientId(clientId) 44 | .clientSecret(clientSecret) 45 | .environment(Environment.SANDBOX) 46 | .build(); 47 | 48 | Long productId = 174L; //From GiftcardProduct.id 49 | Request request; 50 | 51 | try { 52 | request = giftcardAPI.discounts().getByProductId(productId); 53 | } catch (ReloadlyException e) { 54 | // api error retrieving access_token 55 | } 56 | 57 | GiftcardDiscount discount = null; 58 | try { 59 | discount = request.execute(); 60 | } catch (APIException e) { 61 | // api error 62 | } catch (ReloadlyException e) { 63 | // request error 64 | } catch (Exception e) { 65 | // all others 66 | } 67 | ``` 68 | -------------------------------------------------------------------------------- /java-sdk-giftcard/usage/GIFTCARD-ORDER-OPERATIONS.md: -------------------------------------------------------------------------------- 1 | # Giftcard Order Operations 2 | 3 | Use the Order Operations to order a gift card and to retrieve the details of a purchased gift card. 4 | 5 | ## Order - Send a gift card 6 | 7 | In order to send a gift card to a recipient, a couple of information will be required such as the quantity, product id, 8 | name of sender, unit price, the recipient's email or phone number and a custom identifier. 9 | 10 | ```java 11 | GiftcardAPI giftcardAPI = GiftcardAPI.builder() 12 | .clientId(clientId) 13 | .clientSecret(clientSecret) 14 | .environment(Environment.SANDBOX) 15 | .build(); 16 | 17 | int quantity = 3; 18 | Long productId = 13L; 19 | String senderName = "Doctor Vee"; 20 | BigDecimal unitPrice = BigDecimal.valueOf(43.34); 21 | String recipientEmail = "johndoe@gmail.com"; 22 | String customIdentifier = UUID.randomUUID().toString(); // Use your own internal reference 23 | 24 | GiftCardOrderRequest orderRequest = GiftCardOrderRequest.builder() 25 | .quantity(quantity) 26 | .productId(productId) 27 | .senderName(senderName) 28 | .unitPrice(unitPrice) 29 | .recipientEmail(recipientEmail) 30 | .recipientPhone(new GiftCardOrderRequest.Phone("+2348081234567", CountryCode.NG)) // Optional 31 | .customIdentifier(customIdentifier) 32 | .build(); 33 | 34 | Request request = null; 35 | 36 | try { 37 | request = giftcardAPI.orders().placeOrder(orderRequest); 38 | } catch (ReloadlyException e) { 39 | log.error("api error retrieving access token"); 40 | } 41 | 42 | GiftcardTransaction transaction = null; 43 | 44 | try { 45 | assert request != null; 46 | transaction = request.execute(); 47 | } catch (APIException e) { 48 | log.error("Api Error"); 49 | } catch (ReloadlyException e) { 50 | log.error("Request Error"); 51 | } catch (Exception e) { 52 | log.error("Other Errors"); 53 | } 54 | ``` 55 | 56 | ## Orders - Retrieve the details of a gift card 57 | 58 | You can also retrieve the details (card number and pin code) of a gift card that has been earlier purchase. You'd just 59 | have to provide the transaction id. 60 | 61 | ```java 62 | GiftcardAPI giftcardAPI = GiftcardAPI.builder() 63 | .clientId(clientId) 64 | .clientSecret(clientSecret) 65 | .environment(Environment.SANDBOX) 66 | .build(); 67 | 68 | Long transactionId = 3L; 69 | 70 | Request> request = null; 71 | 72 | try { 73 | request = giftcardAPI.orders().redeem(transactionId); 74 | } catch (ReloadlyException e) { 75 | log.error("api error retrieving access token"); 76 | } 77 | 78 | List giftcardInfoList = null; 79 | 80 | try { 81 | assert request != null; 82 | giftcardInfoList = request.execute(); 83 | } catch (APIException e) { 84 | log.error("Api Error"); 85 | } catch (ReloadlyException e) { 86 | log.error("Request Error"); 87 | } catch (Exception e) { 88 | log.error("Other Errors"); 89 | } 90 | 91 | ``` 92 | 93 | -------------------------------------------------------------------------------- /java-sdk-giftcard/usage/GIFTCARD-REDEEM-INSTRUCTIONS-OPERATIONS.md: -------------------------------------------------------------------------------- 1 | # Giftcard Redeem Instructions Operations 2 | 3 | Reloadly provides detailed instructions on how gift cards can be redeemed. The Redeem Instructions Operations allow you 4 | to provide information on how to retrieve these redeem instructions. 5 | 6 | ## Redeem Instructions - List 7 | 8 | You can get a list of the redeem instructions that exist for all the gift card brands. 9 | 10 | ```java 11 | GiftcardAPI giftcardAPI = GiftcardAPI.builder() 12 | .clientId(clientId) 13 | .clientSecret(clientSecret) 14 | .environment(Environment.SANDBOX) 15 | .build(); 16 | 17 | Request> request = null; 18 | 19 | try { 20 | request = giftcardAPI.redeemInstructions().list(); 21 | } catch (ReloadlyException e) { 22 | log.error("api error retrieving access token"); 23 | } 24 | 25 | List redeemInstructions = null; 26 | 27 | try { 28 | assert request != null; 29 | redeemInstructions = request.execute(); 30 | } catch (APIException e) { 31 | log.error("Api Error"); 32 | } catch (ReloadlyException e) { 33 | log.error("Request Error"); 34 | } catch (Exception e) { 35 | log.error("Other Errors"); 36 | } 37 | 38 | ``` 39 | 40 | ## Redeem Instructions - Get by brand id 41 | 42 | You can get a redeem instruction using the brand id 43 | 44 | ```java 45 | GiftcardAPI giftcardAPI = GiftcardAPI.builder() 46 | .clientId(clientId) 47 | .clientSecret(clientSecret) 48 | .environment(Environment.SANDBOX) 49 | .build(); 50 | 51 | Request request = null; 52 | 53 | try { 54 | request = giftcardAPI.redeemInstructions().getByBrandId(23L); 55 | } catch (ReloadlyException e) { 56 | log.error("api error retrieving access token"); 57 | } 58 | 59 | GiftcardRedeemInstruction redeemInstruction = null; 60 | 61 | try { 62 | assert request != null; 63 | redeemInstruction = request.execute(); 64 | } catch (APIException e) { 65 | log.error("Api Error"); 66 | } catch (ReloadlyException e) { 67 | log.error("Request Error"); 68 | } catch (Exception e) { 69 | log.error("Other Errors"); 70 | } 71 | 72 | ``` -------------------------------------------------------------------------------- /lombok.config: -------------------------------------------------------------------------------- 1 | config.stopBubbling = true 2 | lombok.addLombokGeneratedAnnotation = true --------------------------------------------------------------------------------