├── .gitattributes ├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── bug_report.yml │ └── feature_request.yml ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── ci.yml │ └── release.yml ├── .gitignore ├── .gitmodules ├── CHANGELOG.md ├── Gemfile ├── LICENSE ├── Makefile ├── README.md ├── UPGRADE_GUIDE.md ├── VERSION ├── easypost.gemspec ├── lib ├── easypost.rb └── easypost │ ├── client.rb │ ├── connection.rb │ ├── constants.rb │ ├── errors.rb │ ├── errors │ ├── api │ │ ├── api_error.rb │ │ ├── bad_request_error.rb │ │ ├── connection_error.rb │ │ ├── external_api_error.rb │ │ ├── forbidden_error.rb │ │ ├── gateway_timeout_error.rb │ │ ├── internal_server_error.rb │ │ ├── invalid_request_error.rb │ │ ├── method_not_allowed_error.rb │ │ ├── not_found_error.rb │ │ ├── payment_error.rb │ │ ├── proxy_error.rb │ │ ├── rate_limit_error.rb │ │ ├── redirect_error.rb │ │ ├── retry_error.rb │ │ ├── service_unavailable_error.rb │ │ ├── ssl_error.rb │ │ ├── timeout_error.rb │ │ ├── unauthorized_error.rb │ │ └── unknown_api_error.rb │ ├── easy_post_error.rb │ ├── end_of_pagination_error.rb │ ├── filtering_error.rb │ ├── invalid_object_error.rb │ ├── invalid_parameter_error.rb │ ├── missing_parameter_error.rb │ └── signature_verification_error.rb │ ├── hooks.rb │ ├── hooks │ ├── request_context.rb │ └── response_context.rb │ ├── http_client.rb │ ├── internal_utilities.rb │ ├── models.rb │ ├── models │ ├── address.rb │ ├── api_key.rb │ ├── base.rb │ ├── batch.rb │ ├── brand.rb │ ├── carrier_account.rb │ ├── carrier_type.rb │ ├── claim.rb │ ├── customs_info.rb │ ├── customs_item.rb │ ├── end_shipper.rb │ ├── event.rb │ ├── insurance.rb │ ├── order.rb │ ├── parcel.rb │ ├── payload.rb │ ├── payment_method.rb │ ├── pickup.rb │ ├── pickup_rate.rb │ ├── postage_label.rb │ ├── rate.rb │ ├── referral.rb │ ├── refund.rb │ ├── report.rb │ ├── scan_form.rb │ ├── shipment.rb │ ├── tax_identifier.rb │ ├── tracker.rb │ ├── user.rb │ └── webhook.rb │ ├── services.rb │ ├── services │ ├── address.rb │ ├── api_key.rb │ ├── base.rb │ ├── batch.rb │ ├── beta_rate.rb │ ├── beta_referral_customer.rb │ ├── billing.rb │ ├── carrier_account.rb │ ├── carrier_metadata.rb │ ├── carrier_type.rb │ ├── claim.rb │ ├── customs_info.rb │ ├── customs_item.rb │ ├── end_shipper.rb │ ├── event.rb │ ├── insurance.rb │ ├── order.rb │ ├── parcel.rb │ ├── pickup.rb │ ├── rate.rb │ ├── referral_customer.rb │ ├── refund.rb │ ├── report.rb │ ├── scan_form.rb │ ├── shipment.rb │ ├── smart_rate.rb │ ├── tracker.rb │ ├── user.rb │ └── webhook.rb │ ├── util.rb │ ├── utilities │ ├── constants.rb │ ├── json.rb │ ├── static_mapper.rb │ └── system.rb │ └── version.rb └── spec ├── address_spec.rb ├── api_key_spec.rb ├── batch_spec.rb ├── beta_rate_spec.rb ├── beta_referral_customer_spec.rb ├── billing_spec.rb ├── carrier_account_spec.rb ├── carrier_metadata_spec.rb ├── carrier_type_spec.rb ├── cassettes ├── address │ ├── EasyPost_Services_Address_address_service_creates_an_address.yml │ ├── EasyPost_Services_Address_address_service_creates_an_address_with_an_array_verify_param.yml │ ├── EasyPost_Services_Address_address_service_creates_an_address_with_verify_param.yml │ ├── EasyPost_Services_Address_address_service_creates_an_address_with_verify_strict_param.yml │ ├── EasyPost_Services_Address_all_retrieves_all_addresses.yml │ ├── EasyPost_Services_Address_create_and_verify_creates_a_verified_address.yml │ ├── EasyPost_Services_Address_create_and_verify_throws_an_error_for_invalid_address_verification.yml │ ├── EasyPost_Services_Address_get_next_page_retrieves_the_next_page_of_a_collection.yml │ ├── EasyPost_Services_Address_retrieve_retrieves_an_address.yml │ ├── EasyPost_Services_Address_verify_throws_an_error_for_invalid_address_verification.yml │ └── EasyPost_Services_Address_verify_verifies_an_already_created_address.yml ├── api_key │ ├── EasyPost_Services_ApiKey_all_retrieves_all_API_keys.yml │ ├── EasyPost_Services_ApiKey_retrieve_api_keys_for_user_retrieves_child_user_s_API_keys_as_a_parent.yml │ └── EasyPost_Services_ApiKey_retrieve_api_keys_for_user_retrieves_the_authenticated_user_s_API_keys.yml ├── batch │ ├── EasyPost_Services_Batch_add_and_remove_shipments_adds_and_removes_shipments_from_a_batch.yml │ ├── EasyPost_Services_Batch_all_retrieves_all_batches.yml │ ├── EasyPost_Services_Batch_batch_service_create_a_batch.yml │ ├── EasyPost_Services_Batch_buy_buys_a_batch.yml │ ├── EasyPost_Services_Batch_create_scan_form_creates_a_scanform_for_a_batch.yml │ ├── EasyPost_Services_Batch_label_generates_a_label_for_a_batch.yml │ └── EasyPost_Services_Batch_retrieve_retrieves_a_batch.yml ├── beta_rate │ ├── EasyPost_Services_BetaRate_retrieve_stateless_rates_get_lowest_stateless_rate_gets_the_lowest_stateless_rate_for_various_combinations_of_filters.yml │ └── EasyPost_Services_BetaRate_retrieve_stateless_rates_retrieve_all_stateless_rates.yml ├── beta_referral_customer │ ├── EasyPost_Services_BetaReferralCustomer_add_payment_method_adds_a_Stripe_card_or_bank_account_to_a_referral_customer_account.yml │ ├── EasyPost_Services_BetaReferralCustomer_create_bank_account_client_secret_returns_a_client_secret_for_bank_accounts.yml │ ├── EasyPost_Services_BetaReferralCustomer_create_credit_card_client_secret_returns_a_client_secret_for_credit_cards.yml │ ├── EasyPost_Services_BetaReferralCustomer_refund_by_amount_refunds_a_referral_user_by_a_specific_amount.yml │ └── EasyPost_Services_BetaReferralCustomer_refund_by_payment_log_refunds_a_referral_user_by_a_specific_payment_log_entry.yml ├── carrier_account │ ├── EasyPost_Services_CarrierAccount_all_retrieves_all_carrier_accounts.yml │ ├── EasyPost_Services_CarrierAccount_create_creates_a_FedEx_account.yml │ ├── EasyPost_Services_CarrierAccount_create_creates_a_UPS_account.yml │ ├── EasyPost_Services_CarrierAccount_create_creates_a_carrier_account.yml │ ├── EasyPost_Services_CarrierAccount_create_creates_an_Amazon_account.yml │ ├── EasyPost_Services_CarrierAccount_delete_deletes_a_carrier_account.yml │ ├── EasyPost_Services_CarrierAccount_retrieve_retrieves_a_carrier_account.yml │ ├── EasyPost_Services_CarrierAccount_update_updates_a_carrier_account.yml │ └── EasyPost_Services_CarrierAccount_update_updates_an_ups_account.yml ├── carrier_metadata │ ├── EasyPost_Services_CarrierMetadata_retrieve_carrier_metadata_retrieve_metadata_for_a_single_carrier.yml │ └── EasyPost_Services_CarrierMetadata_retrieve_carrier_metadata_retrieve_metadata_for_all_carriers.yml ├── carrier_type │ └── EasyPost_Services_CarrierType_all_retrieves_all_carrier_types.yml ├── claim │ ├── EasyPost_Services_Claim_all_retrieves_all_claim_objects.yml │ ├── EasyPost_Services_Claim_cancel_cancels_an_claim.yml │ ├── EasyPost_Services_Claim_create_creates_a_claim_object.yml │ ├── EasyPost_Services_Claim_get_next_page_retrieves_the_next_page_of_a_collection.yml │ └── EasyPost_Services_Claim_retrieve_retrieves_a_claim_object.yml ├── client │ ├── EasyPost_Client_client_object_allows_Faraday_to_be_used_as_a_custom_client.yml │ ├── EasyPost_Client_client_object_allows_Typhoeus_to_be_used_as_a_custom_client.yml │ ├── EasyPost_Client_client_object_hooks_notifies_multiple_subscribers.yml │ ├── EasyPost_Client_client_object_hooks_removes_subscribers.yml │ ├── EasyPost_Client_client_object_hooks_subscribes_to_request_events.yml │ └── EasyPost_Client_client_object_hooks_subscribes_to_response_events.yml ├── customs_info │ ├── EasyPost_Services_CustomsInfo_create_creates_a_customs_info.yml │ └── EasyPost_Services_CustomsInfo_retrieve_retrieves_a_customs_info.yml ├── customs_item │ ├── EasyPost_Services_CustomsItem_create_creates_a_customs_item.yml │ └── EasyPost_Services_CustomsItem_retrieve_retrieves_a_customs_item.yml ├── end_shipper │ ├── EasyPost_Services_EndShipper_all_retrieves_all_EndShipper_objects.yml │ ├── EasyPost_Services_EndShipper_create_creates_an_EndShipper_object.yml │ ├── EasyPost_Services_EndShipper_retrieve_retrieves_an_EndShipper_object.yml │ └── EasyPost_Services_EndShipper_update_updates_the_EndShipper_object.yml ├── errors │ ├── EasyPost_Errors_api_error_assigns_properties_of_an_error_correctly.yml │ └── EasyPost_Errors_api_error_assigns_properties_of_an_error_correctly_when_returned_via_the_alternative_format.yml ├── event │ ├── EasyPost_Services_Event_all_retrieves_all_events.yml │ ├── EasyPost_Services_Event_get_next_page_retrieves_the_next_page_of_a_collection.yml │ ├── EasyPost_Services_Event_retrieve_a_payload_retrieve_a_payload.yml │ ├── EasyPost_Services_Event_retrieve_all_payloads_retrieve_all_payloads.yml │ └── EasyPost_Services_Event_retrieve_retrieves_an_event.yml ├── insurance │ ├── EasyPost_Services_Insurance_all_retrieves_all_insurance_objects.yml │ ├── EasyPost_Services_Insurance_create_creates_an_insurance_object.yml │ ├── EasyPost_Services_Insurance_get_next_page_retrieves_the_next_page_of_a_collection.yml │ ├── EasyPost_Services_Insurance_refund_cancels_an_insurance.yml │ └── EasyPost_Services_Insurance_retrieve_retrieves_an_insurance_object.yml ├── order │ ├── EasyPost_Services_Order_buy_buys_an_order.yml │ ├── EasyPost_Services_Order_buy_buys_an_order_with_a_rate_object.yml │ ├── EasyPost_Services_Order_create_creates_an_order.yml │ ├── EasyPost_Services_Order_get_rates_retrieves_rates_for_an_order.yml │ ├── EasyPost_Services_Order_lowest_rate_tests_various_usage_alterations_of_the_lowest_rate_method.yml │ └── EasyPost_Services_Order_retrieve_retrieves_an_order.yml ├── parcel │ ├── EasyPost_Services_Parcel_create_creates_a_parcel.yml │ └── EasyPost_Services_Parcel_retrieve_retrieves_a_parcel.yml ├── pickup │ ├── EasyPost_Services_Pickup_all_retrieves_all_pickups.yml │ ├── EasyPost_Services_Pickup_buy_buys_a_pickup.yml │ ├── EasyPost_Services_Pickup_buy_buys_a_pickup_with_a_pickup_rate_object.yml │ ├── EasyPost_Services_Pickup_cancel_cancels_a_pickup.yml │ ├── EasyPost_Services_Pickup_create_creates_a_pickup.yml │ ├── EasyPost_Services_Pickup_get_next_page_retrieves_the_next_page_of_a_collection.yml │ ├── EasyPost_Services_Pickup_lowest_rate_tests_various_usage_alterations_of_the_lowest_rate_method.yml │ └── EasyPost_Services_Pickup_retrieve_retrieves_a_pickup.yml ├── rate │ └── EasyPost_Services_Rate_retrieve_retrieves_a_rate.yml ├── referral_customer │ ├── EasyPost_Services_ReferralCustomer_add_bank_account_from_stripe_raises_an_error_when_adding_a_bank_account_from_Stripe_fails.yml │ ├── EasyPost_Services_ReferralCustomer_add_credit_card_adds_a_credit_card_to_a_referral_customer_account.yml │ ├── EasyPost_Services_ReferralCustomer_add_credit_card_from_stripe_raises_an_error_when_adding_a_credit_card_from_Stripe_fails.yml │ ├── EasyPost_Services_ReferralCustomer_all_retrieve_all_referral_customers.yml │ ├── EasyPost_Services_ReferralCustomer_create_creates_a_referral_customer.yml │ ├── EasyPost_Services_ReferralCustomer_get_next_page_retrieves_the_next_page_of_a_collection.yml │ ├── EasyPost_Services_ReferralCustomer_raises_an_error_when_we_cannot_send_details_to_Stripe.yml │ └── EasyPost_Services_ReferralCustomer_update_email_updates_a_referral_customer.yml ├── refund │ ├── EasyPost_Services_Refund_all_retrieves_all_refunds.yml │ ├── EasyPost_Services_Refund_create_creates_a_refund.yml │ ├── EasyPost_Services_Refund_get_next_page_retrieves_the_next_page_of_a_collection.yml │ └── EasyPost_Services_Refund_retrieve_retrieves_a_refund.yml ├── report │ ├── EasyPost_Services_Report_all_retrieves_all_reports.yml │ ├── EasyPost_Services_Report_create_creates_a_report.yml │ ├── EasyPost_Services_Report_create_creates_a_report_with_custom_additional_columns.yml │ ├── EasyPost_Services_Report_create_creates_a_report_with_custom_columns.yml │ ├── EasyPost_Services_Report_get_next_page_retrieves_the_next_page_of_a_collection.yml │ └── EasyPost_Services_Report_retrieve_retrieves_a_report.yml ├── scan_form │ ├── EasyPost_Services_ScanForm_all_retrieves_all_scanforms.yml │ ├── EasyPost_Services_ScanForm_create_creates_a_scanform.yml │ ├── EasyPost_Services_ScanForm_get_next_page_retrieves_the_next_page_of_a_collection.yml │ └── EasyPost_Services_ScanForm_retrieve_retrieves_a_scanform.yml ├── shipment │ ├── EasyPost_Services_Shipment_all_retrieves_all_shipments.yml │ ├── EasyPost_Services_Shipment_all_stores_the_params_used_to_retrieve_the_shipments.yml │ ├── EasyPost_Services_Shipment_buy_buys_a_shipment.yml │ ├── EasyPost_Services_Shipment_buy_buys_a_shipment_with_end_shipper_id.yml │ ├── EasyPost_Services_Shipment_create_creates_a_shipment.yml │ ├── EasyPost_Services_Shipment_create_creates_a_shipment_when_only_IDs_are_used.yml │ ├── EasyPost_Services_Shipment_create_creates_a_shipment_with_empty_or_nil_objects_and_arrays.yml │ ├── EasyPost_Services_Shipment_create_creates_a_shipment_with_tax_identifiers.yml │ ├── EasyPost_Services_Shipment_generate_form_generates_a_form_for_a_shipment.yml │ ├── EasyPost_Services_Shipment_get_lowest_smartrate_gets_the_lowest_smartrate_from_a_list_of_smartrates.yml │ ├── EasyPost_Services_Shipment_get_lowest_smartrate_raises_an_error_when_no_rates_are_found_due_to_delivery_accuracy.yml │ ├── EasyPost_Services_Shipment_get_lowest_smartrate_raises_an_error_when_no_rates_are_found_due_to_delivery_days.yml │ ├── EasyPost_Services_Shipment_get_next_page_retrieves_the_next_page_of_a_collection.yml │ ├── EasyPost_Services_Shipment_get_smartrates_retrieves_smartrates_of_a_shipment.yml │ ├── EasyPost_Services_Shipment_insure_insures_a_shipment.yml │ ├── EasyPost_Services_Shipment_insure_insures_a_shipment_with_unwrapped_params.yml │ ├── EasyPost_Services_Shipment_label_converts_the_label_format_of_a_shipment.yml │ ├── EasyPost_Services_Shipment_label_converts_the_label_format_of_a_shipment_with_unwrapped_params.yml │ ├── EasyPost_Services_Shipment_lowest_rate_tests_various_usage_alterations_of_the_lowest_rate_method.yml │ ├── EasyPost_Services_Shipment_lowest_rate_tests_various_usage_alterations_of_the_lowest_rate_method_when_excluding_params.yml │ ├── EasyPost_Services_Shipment_lowest_smartrate_gets_the_lowest_smartrate_of_a_shipment.yml │ ├── EasyPost_Services_Shipment_lowest_smartrate_raises_an_error_when_no_rates_are_found_due_to_delivery_accuracy.yml │ ├── EasyPost_Services_Shipment_lowest_smartrate_raises_an_error_when_no_rates_are_found_due_to_delivery_days.yml │ ├── EasyPost_Services_Shipment_refund_refund_a_shipment.yml │ ├── EasyPost_Services_Shipment_regenerate_rates_regenerates_rates_for_a_shipment.yml │ ├── EasyPost_Services_Shipment_retrieve_estimated_delivery_date_retrieve_time-in-transit_data_for_each_of_the_Rates_of_a_shipment.yml │ ├── EasyPost_Services_Shipment_retrieve_recommend_ship_date_retrieve_recommend_ship_date_of_a_shipment.yml │ └── EasyPost_Services_Shipment_retrieve_retrieves_a_shipment.yml ├── smart_rate │ ├── EasyPost_Services_SmartRate_deliver_by_retrieve_the_estimated_delivery_date.yml │ └── EasyPost_Services_SmartRate_deliver_on_retrieve_a_recommended_ship_date.yml ├── tracker │ ├── EasyPost_Services_Tracker_all_retrieves_all_trackers.yml │ ├── EasyPost_Services_Tracker_all_stores_the_params_used_to_retrieve_the_trackers.yml │ ├── EasyPost_Services_Tracker_create_creates_a_tracker.yml │ ├── EasyPost_Services_Tracker_get_next_page_retrieves_the_next_page_of_a_collection.yml │ └── EasyPost_Services_Tracker_retrieve_retrieves_a_tracker.yml ├── user │ ├── EasyPost_Services_User_all_retrieves_all_child_users.yml │ ├── EasyPost_Services_User_create_creates_a_child_user.yml │ ├── EasyPost_Services_User_delete_deletes_a_user.yml │ ├── EasyPost_Services_User_get_next_page_retrieves_the_next_page_of_a_collection.yml │ ├── EasyPost_Services_User_retrieve_me_retrieves_the_authenticated_user.yml │ ├── EasyPost_Services_User_retrieve_retrieves_a_user.yml │ ├── EasyPost_Services_User_update_brand_updates_the_authenticated_user_s_brand.yml │ └── EasyPost_Services_User_update_updates_a_user.yml └── webhook │ ├── EasyPost_Services_Webhook_all_retrieves_all_webhooks.yml │ ├── EasyPost_Services_Webhook_create_creates_a_webhook.yml │ ├── EasyPost_Services_Webhook_delete_deletes_a_webhook.yml │ ├── EasyPost_Services_Webhook_retrieve_retrieves_a_webhook.yml │ └── EasyPost_Services_Webhook_update_updates_a_webhook.yml ├── claim_spec.rb ├── client_spec.rb ├── customs_info_spec.rb ├── customs_item_spec.rb ├── end_shipper_spec.rb ├── errors_spec.rb ├── event_spec.rb ├── insurance_spec.rb ├── order_spec.rb ├── parcel_spec.rb ├── pickup_spec.rb ├── rate_spec.rb ├── referral_customer_spec.rb ├── refund_spec.rb ├── report_spec.rb ├── scan_form_spec.rb ├── shipment_spec.rb ├── smart_rate_spec.rb ├── spec_helper.rb ├── support ├── fixture.rb └── vcr.rb ├── tracker_spec.rb ├── user_spec.rb ├── util_spec.rb └── webhook_spec.rb /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | 3 | # Cassettes 4 | spec/cassettes/**/* -diff 5 | spec/cassettes/**/* linguist-generated 6 | 7 | # Docs 8 | docs/**/* -diff 9 | docs/**/* linguist-generated 10 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Ping the Shippers team for reviews on every PR 2 | * @EasyPost/team-shippers 3 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.yml: -------------------------------------------------------------------------------- 1 | name: Bug Report 2 | description: File a bug report 3 | title: "[Bug]: " 4 | labels: [ "triage" ] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for taking the time to report an issue in this repository. Please fill out the form below. 10 | - type: input 11 | id: software-version 12 | attributes: 13 | label: Software Version 14 | # change this description for the specific repo 15 | description: | 16 | What version of our software are you running? 17 | TIP: [Available versions](https://github.com/EasyPost/easypost-ruby/releases) 18 | validations: 19 | required: true 20 | - type: input 21 | id: language-version 22 | attributes: 23 | label: Language Version 24 | # change this description for the specific language of the repo 25 | description: | 26 | What language version and/or framework are you using? 27 | TIP: [How to find your Ruby version](https://blog.arkency.com/which-ruby-version-am-i-using-how-to-check/) 28 | validations: 29 | required: true 30 | - type: input 31 | id: os 32 | attributes: 33 | label: Operating System 34 | description: What operating system are you running the software on? 35 | validations: 36 | required: true 37 | - type: textarea 38 | id: behavior 39 | attributes: 40 | label: What happened? 41 | description: | 42 | Please describe what happened in reproducible steps. 43 | Include how often you see this issue, and any relevant links (i.e. GitHub issue, Stack Overflow, etc.). 44 | value: | 45 | 1. 46 | 2. 47 | 3. 48 | ... 49 | validations: 50 | required: true 51 | - type: textarea 52 | id: expected-behavior 53 | attributes: 54 | label: What was expected? 55 | description: Please describe what was expected to happen instead. 56 | validations: 57 | required: true 58 | - type: textarea 59 | id: sample-code 60 | attributes: 61 | label: Sample Code 62 | description: | 63 | Please provide any sample code that demonstrates the behavior. 64 | This will be automatically formatted into the appropriate language, so no need for backticks. 65 | **Do not include any private information such as API keys or passwords.** 66 | # change this render to the appropriate language: https://github.com/github/linguist/blob/master/lib/linguist/languages.yml 67 | render: rb 68 | validations: 69 | required: false 70 | - type: textarea 71 | id: logs 72 | attributes: 73 | label: Relevant logs 74 | description: | 75 | Please copy and paste any relevant log output. 76 | This will be automatically formatted into shell output, so no need for backticks. 77 | If you have screenshots instead, please paste them below. 78 | **Do not include any private information such as API keys or passwords.** 79 | render: sh 80 | validations: 81 | required: false 82 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.yml: -------------------------------------------------------------------------------- 1 | name: Feature Request 2 | description: Request a new feature 3 | title: "[Feat]: " 4 | labels: [ "triage" ] 5 | body: 6 | - type: markdown 7 | attributes: 8 | value: | 9 | Thank you for taking the time to request a new feature. 10 | Please note, all feature requests are subject to review and approval. 11 | We welcome all suggestions and ideas, but we cannot guarantee when or if we will implement them. 12 | 13 | We also welcome pull requests, if you would like to implement the feature yourself. 14 | Doing so will likely accelerate the process of implementing the requested feature. 15 | - type: checkboxes 16 | id: searched 17 | attributes: 18 | label: Feature Request Is New 19 | # change issue link below for the specific repo 20 | description: | 21 | Before we begin, please confirm that the requested feature does not already exist or has not [already been requested](https://github.com/EasyPost/easypost-ruby/issues). 22 | options: 23 | - label: I have verified that the requested feature does not already exist or has not already been requested. 24 | required: true 25 | - type: textarea 26 | id: description 27 | attributes: 28 | label: Description of the feature 29 | description: | 30 | Please provide a detailed description of the feature, including: 31 | - What the feature is 32 | - What value it adds to the application 33 | - How it should be implemented (i.e. pseudo-code, or a high-level description of the user experience) 34 | - Any other relevant information 35 | validations: 36 | required: true 37 | 38 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description 2 | 3 | 4 | 5 | # Testing 6 | 7 | 14 | 15 | # Pull Request Type 16 | 17 | Please select the option(s) that are relevant to this PR. 18 | 19 | - [ ] Bug fix (non-breaking change which fixes an issue) 20 | - [ ] New feature (non-breaking change which adds functionality) 21 | - [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected) 22 | - [ ] Improvement (fixing a typo, updating readme, renaming a variable name, etc) 23 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: 'CI' 2 | 3 | on: 4 | push: 5 | branches: [master] 6 | pull_request: ~ 7 | workflow_dispatch: ~ 8 | 9 | jobs: 10 | run-tests: 11 | runs-on: ubuntu-latest 12 | strategy: 13 | matrix: 14 | rubyversion: ['2.7', '3.0', '3.1', '3.2', '3.3', '3.4'] 15 | steps: 16 | - uses: actions/checkout@v4 17 | - uses: ruby/setup-ruby@v1 18 | with: 19 | ruby-version: ${{ matrix.rubyversion }} 20 | rubygems: '3.3.0' 21 | bundler-cache: true 22 | - name: Install Dependencies 23 | run: make install 24 | - name: run tests 25 | run: EASYPOST_TEST_API_KEY=123 EASYPOST_PROD_API_KEY=123 make test 26 | - name: Coveralls 27 | if: github.ref == 'refs/heads/master' 28 | uses: coverallsapp/github-action@master 29 | with: 30 | github-token: ${{ secrets.GITHUB_TOKEN }} 31 | path-to-lcov: './coverage/lcov/easypost-ruby.lcov' 32 | lint: 33 | runs-on: ubuntu-latest 34 | steps: 35 | - uses: actions/checkout@v4 36 | - uses: ruby/setup-ruby@v1 37 | with: 38 | ruby-version: '3.4' 39 | rubygems: '3.3.0' 40 | bundler-cache: true 41 | - name: Install Dependencies 42 | run: make install 43 | - name: Install style guides 44 | run: make install-styleguide 45 | - name: Lint Project 46 | run: make lint 47 | docs: 48 | if: github.ref == 'refs/heads/master' 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v4 52 | - uses: ruby/setup-ruby@v1 53 | with: 54 | ruby-version: '3.4' 55 | rubygems: '3.3.0' 56 | bundler-cache: true 57 | - name: Install Dependencies 58 | run: make install 59 | - name: Generate Docs 60 | run: make docs 61 | - name: Deploy docs 62 | uses: peaceiris/actions-gh-pages@v3 63 | with: 64 | github_token: ${{ secrets.GITHUB_TOKEN }} 65 | publish_dir: docs 66 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: release 2 | 3 | on: 4 | release: 5 | types: [published] 6 | workflow_dispatch: ~ 7 | 8 | jobs: 9 | release: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v4 13 | - uses: ruby/setup-ruby@v1 14 | with: 15 | ruby-version: '3.4' 16 | rubygems: '3.3.0' 17 | bundler-cache: true 18 | - name: Build Package 19 | run: make install build 20 | - name: Publish to RubyGems 21 | run: | 22 | mkdir -p $HOME/.gem 23 | touch $HOME/.gem/credentials 24 | chmod 0600 $HOME/.gem/credentials 25 | printf -- "---\n:rubygems_api_key: ${RUBYGEMS_API_KEY}\n" > $HOME/.gem/credentials 26 | gem push dist/*.gem 27 | env: 28 | RUBYGEMS_API_KEY: '${{secrets.RUBYGEMS_API_KEY}}' 29 | - name: Upload assets to release 30 | uses: AButler/upload-release-assets@v3.0 31 | with: 32 | files: 'dist/*' 33 | repo-token: ${{ secrets.GITHUB_TOKEN }} 34 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ._* 2 | .AppleDouble 3 | .bundle 4 | .config 5 | .DS_Store 6 | .env 7 | .idea/ 8 | .LSOverride 9 | .Spotlight-V100 10 | .Trashes 11 | *.gem 12 | *.rbc 13 | coverage 14 | dist 15 | docs 16 | Gemfile.lock 17 | Icon 18 | InstalledFiles 19 | lib/bundler/man 20 | pkg 21 | rdoc 22 | spec/reports 23 | test/tmp 24 | test/version_tmp 25 | tmp 26 | vendor/ 27 | /easycop.yml 28 | /.rubocop.yml 29 | [._]*.s[a-v][a-z] 30 | [._]*.sw[a-p] 31 | [._]s[a-rt-v][a-z] 32 | [._]ss[a-gi-z] 33 | [._]sw[a-p] 34 | Session.vim 35 | Sessionx.vim 36 | .netrwhist 37 | *~ 38 | tags 39 | [._]*.un~ 40 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "examples"] 2 | path = examples 3 | url = https://github.com/EasyPost/examples 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | source 'https://rubygems.org' 4 | gemspec 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Copyright (c) 2013 EasyPost (Simpler Postage, Inc) 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 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | ## help - Display help about make targets for this Makefile 2 | help: 3 | @cat Makefile | grep '^## ' --color=never | cut -c4- | sed -e "`printf 's/ - /\t- /;'`" | column -s "`printf '\t'`" -t 4 | 5 | ## build - Builds the project 6 | build: 7 | gem build easypost.gemspec --strict 8 | mkdir -p dist 9 | mv *.gem dist/ 10 | 11 | ## clean - Cleans the project 12 | clean: 13 | rm -rf coverage doc *.gem dist 14 | 15 | ## coverage - Generate a test coverage report 16 | coverage: test 17 | 18 | ## docs - Generate documentation for the library 19 | docs: 20 | bundle exec rdoc lib -o docs --title "EasyPost Ruby Docs" 21 | 22 | ## install-styleguide - Import the style guides (Unix only) 23 | install-styleguide: | init-examples-submodule 24 | sh examples/symlink_directory_files.sh examples/style_guides/ruby . 25 | 26 | ## init-examples-submodule - Initialize the examples submodule 27 | init-examples-submodule: 28 | git submodule init 29 | git submodule update 30 | 31 | ## install - Install globally from source 32 | install: | init-examples-submodule 33 | bundle install 34 | 35 | ## lint - Lint the project 36 | lint: rubocop scan 37 | 38 | ## lint-fix - Fix Rubocop errors 39 | lint-fix: rubocop-fix 40 | 41 | ## publish - Publishes the built gem to Rubygems 42 | publish: 43 | gem push dist/*.gem 44 | 45 | ## release - Cuts a release for the project on GitHub (requires GitHub CLI) 46 | # tag = The associated tag title of the release 47 | # target = Target branch or full commit SHA 48 | release: 49 | gh release create ${tag} dist/* --target ${target} 50 | 51 | ## rubocop - lints the project with rubocop 52 | rubocop: 53 | bundle exec rubocop 54 | 55 | ## rubocop-fix - fix rubocop errors 56 | rubocop-fix: 57 | bundle exec rubocop -a 58 | 59 | ## scan - Runs security analysis on the project with Brakeman 60 | scan: 61 | bundle exec brakeman lib --force 62 | 63 | ## test - Test the project (and ignore warnings for test output) 64 | test: 65 | bundle exec rspec 66 | 67 | ## update - Updates dependencies 68 | update: | update-examples-submodule 69 | 70 | ## update-examples-submodule - Update the examples submodule 71 | update-examples-submodule: 72 | git submodule init 73 | git submodule update --remote 74 | 75 | .PHONY: help build clean coverage docs install install-styleguide lint lint-fix publish release rubocop rubocop-fix scan test update update-examples-submodule 76 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 7.0.1 2 | -------------------------------------------------------------------------------- /easypost.gemspec: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | lib = File.expand_path('lib', __dir__) 4 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 5 | require 'easypost/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'easypost' 9 | spec.version = EasyPost::VERSION 10 | spec.license = 'MIT' 11 | spec.summary = 'EasyPost Ruby Client Library' 12 | spec.description = 'Client library for accessing the EasyPost shipping API via Ruby.' 13 | spec.authors = 'EasyPost Developers' 14 | spec.email = 'oss@easypost.com' 15 | spec.homepage = 'https://www.easypost.com/docs' 16 | 17 | spec.files = `git ls-files -z`.split("\x0").reject do |f| 18 | f.match(%r{^(docs|examples|spec)/}) 19 | end 20 | spec.require_paths = ['lib'] 21 | spec.required_ruby_version = '>= 2.7' 22 | 23 | spec.add_development_dependency 'abbrev', '~> 0.1' 24 | spec.add_development_dependency 'benchmark', '~> 0.4' 25 | spec.add_development_dependency 'bigdecimal', '~> 3' 26 | spec.add_development_dependency 'brakeman', '~> 5.4' # can't upgrade to 6.x, requires Ruby 3.0 27 | spec.add_development_dependency 'faraday', '~> 2.8' # can't upgrade to 2.9, requires Ruby 3.0 28 | spec.add_development_dependency 'logger', '~> 1' 29 | spec.add_development_dependency 'ostruct', '~> 0.6' 30 | spec.add_development_dependency 'rdoc', '~> 6.12' 31 | spec.add_development_dependency 'rspec', '~> 3.13' 32 | spec.add_development_dependency 'rubocop', '= 1.72' # TODO: v1.73 no longer packages certain plugins and will require changes 33 | spec.add_development_dependency 'rubocop-rspec', '= 2.31' # can't upgrade to 3.0, requires easycop config changes 34 | spec.add_development_dependency 'simplecov', '~> 0.22' 35 | spec.add_development_dependency 'simplecov-lcov', '~> 0.8' 36 | spec.add_development_dependency 'typhoeus', '~> 1.4' 37 | spec.add_development_dependency 'vcr', '~> 6.3' 38 | spec.add_development_dependency 'webmock', '~> 3.25' 39 | end 40 | -------------------------------------------------------------------------------- /lib/easypost.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'base64' 4 | require 'cgi' 5 | require 'net/http' 6 | 7 | require 'easypost/version' 8 | require 'easypost/connection' 9 | require 'easypost/util' 10 | 11 | # Client 12 | require 'easypost/client' 13 | require 'easypost/http_client' 14 | 15 | # Services 16 | require 'easypost/services' 17 | 18 | # Models 19 | require 'easypost/models' 20 | 21 | # Exceptions 22 | require 'easypost/errors' 23 | 24 | # Internal Utilities 25 | require 'easypost/internal_utilities' 26 | 27 | # Hooks 28 | require 'easypost/hooks' 29 | 30 | module EasyPost 31 | end 32 | -------------------------------------------------------------------------------- /lib/easypost/connection.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | EasyPost::Connection = Struct.new(:uri, :config, keyword_init: true) do 4 | # Make an HTTP request with Ruby's {Net::HTTP} 5 | # 6 | # @param method [Symbol] the HTTP Verb (get, method, put, post, etc.) 7 | # @param path [String] URI path of the resource 8 | # @param requested_api_key [String] ({EasyPost.api_key}) key set Authorization header. 9 | # @param body [String] (nil) body of the request 10 | # @raise [EasyPost::Errors::EasyPostError] if the response has a non-2xx status code 11 | # @return [Hash] JSON object parsed from the response body 12 | def call(method, path, api_key = nil, body = nil) 13 | raise EasyPost::Errors::MissingParameterError.new('api_key') if api_key.nil? 14 | 15 | connection = 16 | if config[:proxy] 17 | proxy_uri = URI(config[:proxy]) 18 | Net::HTTP.new( 19 | uri.host, 20 | uri.port, 21 | proxy_uri.host, 22 | proxy_uri.port, 23 | proxy_uri.user, 24 | proxy_uri.password, 25 | ) 26 | else 27 | Net::HTTP.new(uri.host, uri.port) 28 | end 29 | 30 | connection.use_ssl = true 31 | 32 | config.each do |name, value| 33 | # Discrepancies between RestClient and Net::HTTP. 34 | case name 35 | when :verify_ssl 36 | name = :verify_mode 37 | when :timeout 38 | name = :read_timeout 39 | end 40 | 41 | # Handled in the creation of the client. 42 | if name == :proxy 43 | next 44 | end 45 | 46 | connection.public_send("#{name}=", value) 47 | end 48 | 49 | request = Net::HTTP.const_get(method.capitalize).new(path) 50 | request.body = JSON.dump(EasyPost::InternalUtilities.objects_to_ids(body)) if body 51 | 52 | EasyPost.default_headers.each_pair { |h, v| request[h] = v } 53 | request['Authorization'] = EasyPost.authorization(api_key) 54 | 55 | response = connection.request(request) 56 | response_is_json = response['Content-Type'] ? response['Content-Type'].start_with?('application/json') : false 57 | 58 | EasyPost.parse_response( 59 | status: response.code.to_i, 60 | body: response.body, 61 | json: response_is_json, 62 | ) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /lib/easypost/constants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Constants 4 | API_ERROR_DETAILS_PARSING_ERROR = 'API error details could not be parsed.' 5 | INVALID_PARAMETER = '%s is not a valid parameter.' 6 | INVALID_PAYMENT_METHOD = 'The chosen payment method is not valid. Please try again.' 7 | MISSING_REQUIRED_PARAMETER = 'Required parameter %s is missing.' 8 | NO_MATCHING_RATES = 'No matching rates found.' 9 | NO_USER_FOUND = 'No user found with the given id.' 10 | NO_MORE_PAGES = 'There are no more pages to retrieve.' 11 | NO_PAYMENT_METHODS = 'Billing has not been setup for this user. Please add a payment method.' 12 | STRIPE_CARD_CREATE_FAILED = 'Could not send card details to Stripe, please try again later.' 13 | UNEXPECTED_HTTP_STATUS_CODE = 'Unexpected HTTP status code received: %s' 14 | WEBHOOK_MISSING_SIGNATURE = 'Webhook received does not contain an HMAC signature.' 15 | WEBHOOK_SIGNATURE_MISMATCH = 'Webhook received did not originate from EasyPost or had a webhook secret mismatch.' 16 | end 17 | -------------------------------------------------------------------------------- /lib/easypost/errors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::Errors 4 | end 5 | 6 | require_relative 'errors/easy_post_error' 7 | require_relative 'errors/end_of_pagination_error' 8 | require_relative 'errors/api/external_api_error' 9 | require_relative 'errors/filtering_error' 10 | require_relative 'errors/invalid_object_error' 11 | require_relative 'errors/invalid_parameter_error' 12 | require_relative 'errors/missing_parameter_error' 13 | require_relative 'errors/signature_verification_error' 14 | require_relative 'errors/api/api_error' 15 | require_relative 'errors/api/bad_request_error' 16 | require_relative 'errors/api/connection_error' 17 | require_relative 'errors/api/forbidden_error' 18 | require_relative 'errors/api/gateway_timeout_error' 19 | require_relative 'errors/api/internal_server_error' 20 | require_relative 'errors/api/invalid_request_error' 21 | require_relative 'errors/api/method_not_allowed_error' 22 | require_relative 'errors/api/not_found_error' 23 | require_relative 'errors/api/payment_error' 24 | require_relative 'errors/api/proxy_error' 25 | require_relative 'errors/api/rate_limit_error' 26 | require_relative 'errors/api/redirect_error' 27 | require_relative 'errors/api/retry_error' 28 | require_relative 'errors/api/service_unavailable_error' 29 | require_relative 'errors/api/ssl_error' 30 | require_relative 'errors/api/timeout_error' 31 | require_relative 'errors/api/unauthorized_error' 32 | require_relative 'errors/api/unknown_api_error' 33 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/bad_request_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::BadRequestError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/connection_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::ConnectionError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/external_api_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Errors::ExternalApiError < EasyPost::Errors::EasyPostError 4 | attr_reader :status_code 5 | 6 | def initialize(message, status_code = nil) 7 | super message 8 | @status_code = status_code 9 | end 10 | 11 | def pretty_print 12 | if status_code.nil? 13 | return message 14 | end 15 | 16 | "(#{status_code}): #{message}" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/forbidden_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::ForbiddenError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/gateway_timeout_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::GatewayTimeoutError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/internal_server_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::InternalServerError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/invalid_request_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::InvalidRequestError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/method_not_allowed_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::MethodNotAllowedError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/not_found_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::NotFoundError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/payment_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::PaymentError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/proxy_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::ProxyError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/rate_limit_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::RateLimitError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/redirect_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::RedirectError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/retry_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::RetryError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/service_unavailable_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::ServiceUnavailableError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/ssl_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::SslError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/timeout_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::TimeoutError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/unauthorized_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::UnauthorizedError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/api/unknown_api_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'api_error' 4 | 5 | class EasyPost::Errors::UnknownApiError < EasyPost::Errors::ApiError 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/errors/easy_post_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Errors::EasyPostError < StandardError 4 | def pretty_print 5 | message.to_s 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/easypost/errors/end_of_pagination_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Errors::EndOfPaginationError < EasyPost::Errors::EasyPostError 4 | def initialize 5 | super EasyPost::Constants::NO_MORE_PAGES 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /lib/easypost/errors/filtering_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Errors::FilteringError < EasyPost::Errors::EasyPostError 4 | end 5 | -------------------------------------------------------------------------------- /lib/easypost/errors/invalid_object_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Errors::InvalidObjectError < EasyPost::Errors::EasyPostError 4 | end 5 | -------------------------------------------------------------------------------- /lib/easypost/errors/invalid_parameter_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'easypost/constants' 4 | 5 | class EasyPost::Errors::InvalidParameterError < EasyPost::Errors::EasyPostError 6 | # @param [String] parameter The name of the parameter that was invalid. 7 | # @param [String] suggestion Optional suggestion message for a valid parameter. 8 | def initialize(parameter, suggestion = nil) 9 | super EasyPost::Constants::INVALID_PARAMETER % parameter + (suggestion.nil? ? '' : " #{suggestion}") 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/easypost/errors/missing_parameter_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'easypost/constants' 4 | 5 | class EasyPost::Errors::MissingParameterError < EasyPost::Errors::EasyPostError 6 | def initialize(parameter) 7 | super EasyPost::Constants::MISSING_REQUIRED_PARAMETER % parameter 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/easypost/errors/signature_verification_error.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Errors::SignatureVerificationError < EasyPost::Errors::EasyPostError 4 | end 5 | -------------------------------------------------------------------------------- /lib/easypost/hooks.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::Hooks 4 | def self.subscribe(type, name, block) 5 | subscribers[type][name] = block 6 | 7 | name 8 | end 9 | 10 | def self.unsubscribe(type, name) 11 | subscribers[type].delete(name) 12 | end 13 | 14 | def self.unsubscribe_all(type) 15 | subscribers.delete(type) 16 | end 17 | 18 | def self.notify(type, context) 19 | subscribers[type].each_value { |subscriber| subscriber.call(context) } 20 | end 21 | 22 | def self.any_subscribers?(type) 23 | !subscribers[type].empty? 24 | end 25 | 26 | def self.subscribers 27 | @subscribers ||= Hash.new { |hash, key| hash[key] = {} } 28 | end 29 | 30 | private_class_method :subscribers 31 | end 32 | 33 | require_relative 'hooks/request_context' 34 | require_relative 'hooks/response_context' 35 | -------------------------------------------------------------------------------- /lib/easypost/hooks/request_context.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Hooks::RequestContext 4 | attr_reader :method, :path, :headers, :request_body, :request_timestamp, :request_uuid 5 | 6 | def initialize(method:, path:, headers:, request_body:, request_timestamp:, request_uuid:) 7 | @method = method 8 | @path = path 9 | @headers = headers 10 | @request_body = request_body 11 | @request_timestamp = request_timestamp 12 | @request_uuid = request_uuid 13 | 14 | freeze 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/easypost/hooks/response_context.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Hooks::ResponseContext 4 | attr_reader :http_status, :method, :path, :headers, :response_body, 5 | :request_timestamp, :response_timestamp, :request_uuid, 6 | :client_response_object 7 | 8 | def initialize(http_status:, method:, path:, headers:, response_body:, 9 | request_timestamp:, response_timestamp:, request_uuid:, 10 | client_response_object:) 11 | @http_status = http_status 12 | @method = method 13 | @path = path 14 | @headers = headers 15 | @response_body = response_body 16 | @request_timestamp = request_timestamp 17 | @response_timestamp = response_timestamp 18 | @request_uuid = request_uuid 19 | @client_response_object = client_response_object 20 | 21 | freeze 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /lib/easypost/internal_utilities.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::InternalUtilities 4 | # Convert a string to snake case 5 | def self.to_snake_case(str) 6 | str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2') 7 | .gsub(/([a-z\d])([A-Z])/, '\1_\2') 8 | .downcase 9 | end 10 | 11 | # Form-encode a multi-layer dictionary to a one-layer dictionary. 12 | def self.form_encode_params(hash, parent_keys = [], parent_dict = {}) 13 | result = parent_dict or {} 14 | keys = parent_keys or [] 15 | 16 | hash.each do |key, value| 17 | if value.instance_of?(Hash) 18 | keys << key 19 | result = form_encode_params(value, keys, result) 20 | else 21 | dict_key = build_dict_key(keys + [key]) 22 | result[dict_key] = value 23 | end 24 | end 25 | result 26 | end 27 | 28 | # Build a dict key from a list of keys. 29 | # Example: [code, number] -> code[number] 30 | def self.build_dict_key(keys) 31 | result = keys[0].to_s 32 | 33 | keys[1..].each do |key| 34 | result += "[#{key}]" 35 | end 36 | 37 | result 38 | end 39 | 40 | # Converts an object to an object ID. 41 | def self.objects_to_ids(obj) 42 | case obj 43 | when EasyPost::Models::EasyPostObject 44 | { id: obj.id } 45 | when Hash 46 | result = {} 47 | obj.each { |k, v| result[k] = objects_to_ids(v) unless v.nil? } 48 | result 49 | when Array 50 | obj.map { |v| objects_to_ids(v) } 51 | else 52 | obj 53 | end 54 | end 55 | 56 | # Normalizes a list of strings. 57 | def self.normalize_string_list(lst) 58 | lst = lst.is_a?(String) ? lst.split(',') : Array(lst) 59 | lst.map(&:to_s).map(&:downcase).map(&:strip) 60 | end 61 | end 62 | 63 | require_relative 'utilities/json' 64 | require_relative 'utilities/system' 65 | require_relative 'utilities/static_mapper' 66 | require_relative 'utilities/constants' 67 | -------------------------------------------------------------------------------- /lib/easypost/models.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::Models 4 | end 5 | 6 | require_relative 'models/base' # Must be imported first before the rest of child models 7 | require_relative 'models/address' 8 | require_relative 'models/api_key' 9 | require_relative 'models/batch' 10 | require_relative 'models/brand' 11 | require_relative 'models/carrier_account' 12 | require_relative 'models/carrier_type' 13 | require_relative 'models/claim' 14 | require_relative 'models/customs_info' 15 | require_relative 'models/customs_item' 16 | require_relative 'models/end_shipper' 17 | require_relative 'models/event' 18 | require_relative 'models/insurance' 19 | require_relative 'models/order' 20 | require_relative 'models/parcel' 21 | require_relative 'models/payload' 22 | require_relative 'models/payment_method' 23 | require_relative 'models/pickup_rate' 24 | require_relative 'models/pickup' 25 | require_relative 'models/postage_label' 26 | require_relative 'models/rate' 27 | require_relative 'models/referral' 28 | require_relative 'models/refund' 29 | require_relative 'models/report' 30 | require_relative 'models/scan_form' 31 | require_relative 'models/shipment' 32 | require_relative 'models/tax_identifier' 33 | require_relative 'models/tracker' 34 | require_relative 'models/user' 35 | require_relative 'models/webhook' 36 | -------------------------------------------------------------------------------- /lib/easypost/models/address.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Address objects are used to represent people, places, and organizations in a number of contexts. 4 | class EasyPost::Models::Address < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/api_key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # An ApiKey object that has your EasyPost API. 4 | class EasyPost::Models::ApiKey < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'json' 4 | 5 | # The base class for all JSON objects in the library. 6 | class EasyPost::Models::Object 7 | def initialize(data) 8 | @values = data 9 | add_properties(data) 10 | end 11 | 12 | # Convert to a string. 13 | def to_s(*_args) 14 | JSON.dump(@values) 15 | end 16 | 17 | # Convert object to hash 18 | def to_hash 19 | JSON.parse(JSON.dump(@values)) 20 | end 21 | 22 | # Get element of an array. 23 | def [](key) 24 | @values[key.to_s] 25 | end 26 | 27 | # Set the element of an array. 28 | def []=(key, value) 29 | send(:"#{key}=", value) 30 | end 31 | 32 | private 33 | 34 | def add_properties(values) 35 | values.each_key do |key| 36 | next if key == EasyPost::InternalUtilities::Constants::FILTERS_KEY 37 | 38 | define_singleton_method(key) { handle_value(@values[key]) } # getter 39 | define_singleton_method("#{key}=") { |v| @values[key] = handle_value(v) } # setter 40 | end 41 | end 42 | 43 | def handle_value(val) 44 | case val 45 | when Hash 46 | type = EasyPost::InternalUtilities::StaticMapper::BY_TYPE[val['object']] if val['object'] 47 | prefix = EasyPost::InternalUtilities::StaticMapper::BY_PREFIX[val['id'].split('_').first] if val['id'] 48 | cls = type || prefix || EasyPost::Models::EasyPostObject 49 | cls.new(val) 50 | when Array 51 | val.map { |item| handle_value(item) } 52 | else 53 | val 54 | end 55 | end 56 | end 57 | 58 | # The base class for all API objects in the library that have an ID (plus optional timestamps). 59 | class EasyPost::Models::EasyPostObject < EasyPost::Models::Object 60 | end 61 | -------------------------------------------------------------------------------- /lib/easypost/models/batch.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The Batch object allows you to perform operations on multiple Shipments at once. 4 | class EasyPost::Models::Batch < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/brand.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The Brand object allows you to customize the publicly-accessible html page that shows tracking details for every EasyPost tracker. 4 | class EasyPost::Models::Brand < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/carrier_account.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A CarrierAccount encapsulates your credentials with the carrier. 4 | class EasyPost::Models::CarrierAccount < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/carrier_type.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A CarrierType details the valid fields for a CarrierAccount. 4 | class EasyPost::Models::CarrierType < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/claim.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The Claim object has all the details for the filed claims 4 | class EasyPost::Models::Claim < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/customs_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # CustomsInfo objects contain CustomsItem objects and all necessary information for the generation of customs forms required for international shipping. 4 | class EasyPost::Models::CustomsInfo < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/customs_item.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A CustomsItem object describes goods for international shipment and should be created then included in a CustomsInfo object. 4 | class EasyPost::Models::CustomsItem < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/end_shipper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # EndShipper objects are fully-qualified Address objects that require all parameters and get verified upon creation. 4 | class EasyPost::Models::EndShipper < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/event.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Webhook Events are triggered by changes in objects you've created via the API. 4 | class EasyPost::Models::Event < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/insurance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # An Insurance object represents insurance for packages purchased both via the EasyPost API as well 4 | # as shipments purchased through third parties and later registered with EasyPost. 5 | class EasyPost::Models::Insurance < EasyPost::Models::EasyPostObject 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/models/order.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The Order object represents a collection of packages and can be used for Multi-Piece Shipments. 4 | class EasyPost::Models::Order < EasyPost::Models::EasyPostObject 5 | # Get the lowest rate of an Order (can exclude by having `'!'` as the first element of your optional filter lists). 6 | def lowest_rate(carriers = [], services = []) 7 | EasyPost::Util.get_lowest_object_rate(self, carriers, services) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/easypost/models/parcel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Parcel objects represent the physical container being shipped. 4 | class EasyPost::Models::Parcel < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/payload.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Webhook Event Payloads are triggered by changes in objects you've created via the API. 4 | class EasyPost::Models::Payload < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/payment_method.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # PaymentMethod objects represent a payment method of a user. 4 | class EasyPost::Models::PaymentMethod < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/pickup.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The Pickup object allows you to schedule a pickup from your carrier from your customer's residence or place of business. 4 | class EasyPost::Models::Pickup < EasyPost::Models::EasyPostObject 5 | # Get the lowest rate of a Pickup (can exclude by having `'!'` as the first element of your optional filter lists). 6 | def lowest_rate(carriers = [], services = []) 7 | EasyPost::Util.get_lowest_object_rate(self, carriers, services, 'pickup_rates') 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /lib/easypost/models/pickup_rate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A PickupRate is the rate available for a Pickup. 4 | class EasyPost::Models::PickupRate < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/postage_label.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # PostageLabel is the object containing details about the shipping label. 4 | class EasyPost::Models::PostageLabel < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/rate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A Rate object contains all the details about the rate of a Shipment. 4 | class EasyPost::Models::Rate < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/referral.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # ReferralCustomer objects are User objects created from a Partner user. 4 | class EasyPost::Models::Referral < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/refund.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The Refund object contains details about the Refund of a Shipment. 4 | class EasyPost::Models::Refund < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/report.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A Report contains a csv that is a log of all the objects created within a certain time frame. 4 | class EasyPost::Models::Report < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/scan_form.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A ScanForm can be created to speed up and simplify the carrier pickup process. The ScanForm is one document that can 4 | # be scanned to mark all included tracking codes as "Accepted for Shipment" by the carrier. 5 | class EasyPost::Models::ScanForm < EasyPost::Models::EasyPostObject 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/models/shipment.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The workhorse of the EasyPost API, a Shipment is made up of a "to" and "from" Address, the Parcel 4 | # being shipped, and any customs forms required for international deliveries. 5 | class EasyPost::Models::Shipment < EasyPost::Models::EasyPostObject 6 | # Get the lowest rate of a Shipment (can exclude by having `'!'` as the first element of your optional filter lists). 7 | def lowest_rate(carriers = [], services = []) 8 | EasyPost::Util.get_lowest_object_rate(self, carriers, services) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/easypost/models/tax_identifier.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # TaxIdentifiers are identifying numbers or IDs that are used to charge a specific party when 4 | # dealing with the importing or exporting of good across international borders. 5 | class EasyPost::Models::TaxIdentifier < EasyPost::Models::EasyPostObject 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/models/tracker.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # A Tracker object contains all of the tracking information for a package. 4 | class EasyPost::Models::Tracker < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # The User object can be used to manage your own account and to create child accounts. 4 | class EasyPost::Models::User < EasyPost::Models::EasyPostObject 5 | end 6 | -------------------------------------------------------------------------------- /lib/easypost/models/webhook.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Each Webhook contains the url which EasyPost will notify whenever an object in our system updates. Several types of objects are processed 4 | # asynchronously in the EasyPost system, so whenever an object updates, an Event is sent via HTTP POST to each configured webhook URL. 5 | class EasyPost::Models::Webhook < EasyPost::Models::EasyPostObject 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/services.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::Services 4 | end 5 | 6 | require_relative 'models' 7 | require_relative 'services/base' # Must be imported first before the rest of child services 8 | require_relative 'services/address' 9 | require_relative 'services/api_key' 10 | require_relative 'services/batch' 11 | require_relative 'services/beta_rate' 12 | require_relative 'services/beta_referral_customer' 13 | require_relative 'services/billing' 14 | require_relative 'services/carrier_account' 15 | require_relative 'services/carrier_metadata' 16 | require_relative 'services/carrier_type' 17 | require_relative 'services/claim' 18 | require_relative 'services/customs_info' 19 | require_relative 'services/customs_item' 20 | require_relative 'services/end_shipper' 21 | require_relative 'services/event' 22 | require_relative 'services/insurance' 23 | require_relative 'services/order' 24 | require_relative 'services/parcel' 25 | require_relative 'services/pickup' 26 | require_relative 'services/rate' 27 | require_relative 'services/referral_customer' 28 | require_relative 'services/refund' 29 | require_relative 'services/report' 30 | require_relative 'services/scan_form' 31 | require_relative 'services/shipment' 32 | require_relative 'services/smart_rate' 33 | require_relative 'services/tracker' 34 | require_relative 'services/user' 35 | require_relative 'services/webhook' 36 | -------------------------------------------------------------------------------- /lib/easypost/services/address.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Address < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Address # :nodoc: 5 | 6 | # Create an address. 7 | def create(params = {}) 8 | address = params.reject { |k, _| [:verify, :verify_strict].include?(k) } 9 | 10 | wrapped_params = { address: address } 11 | 12 | if params[:verify] 13 | wrapped_params[:verify] = params[:verify] 14 | end 15 | 16 | if params[:verify_strict] 17 | wrapped_params[:verify_strict] = params[:verify_strict] 18 | end 19 | 20 | response = @client.make_request(:post, 'addresses', params) 21 | 22 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 23 | end 24 | 25 | # Create and verify an Address in one call. 26 | def create_and_verify(params = {}) 27 | wrapped_params = {} 28 | wrapped_params[:address] = params 29 | 30 | response = @client.make_request(:post, 'addresses/create_and_verify', wrapped_params) 31 | 32 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS).address 33 | end 34 | 35 | # Verify an Address. 36 | def verify(id) 37 | response = @client.make_request(:get, "addresses/#{id}/verify") 38 | 39 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS).address 40 | end 41 | 42 | # Retrieve an Address. 43 | def retrieve(id) 44 | response = @client.make_request(:get, "addresses/#{id}") 45 | 46 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 47 | end 48 | 49 | # Retrieve all Addresses. 50 | def all(params = {}) 51 | filters = { key: 'addresses' } 52 | 53 | get_all_helper('addresses', MODEL_CLASS, params, filters) 54 | end 55 | 56 | # Get the next page of addresses. 57 | def get_next_page(collection, page_size = nil) 58 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 59 | 60 | params = { before_id: collection.addresses.last.id } 61 | params[:page_size] = page_size unless page_size.nil? 62 | 63 | all(params) 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/easypost/services/api_key.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::ApiKey < EasyPost::Services::Service 4 | # Retrieve a list of all ApiKey objects. 5 | def all 6 | response = @client.make_request(:get, 'api_keys') 7 | 8 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::ApiKey) 9 | end 10 | 11 | # Retrieve a list of ApiKey objects (works for the authenticated user or a child user). 12 | def retrieve_api_keys_for_user(id) 13 | api_keys = all 14 | 15 | if api_keys.id == id 16 | # This function was called on the authenticated user 17 | return api_keys.keys 18 | end 19 | 20 | # This function was called on a child user (authenticated as parent, only return this child user's details). 21 | api_keys.children.each do |child| 22 | if child.id == id 23 | return child.keys 24 | end 25 | end 26 | 27 | raise EasyPost::Errors::FilteringError.new(EasyPost::Constants::NO_USER_FOUND) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/easypost/services/base.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../internal_utilities' 4 | 5 | # The base class for all services in the library. 6 | class EasyPost::Services::Service 7 | def initialize(client) 8 | @client = client 9 | end 10 | 11 | protected 12 | 13 | def get_all_helper(endpoint, cls, params, filters = nil, beta = false) 14 | response = @client.make_request( 15 | :get, endpoint, params, 16 | beta ? 'beta' : EasyPost::InternalUtilities::Constants::API_VERSION, 17 | ) 18 | 19 | response[EasyPost::InternalUtilities::Constants::FILTERS_KEY] = filters unless filters.nil? 20 | 21 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, cls) 22 | end 23 | 24 | def more_pages?(collection) 25 | collection&.has_more 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /lib/easypost/services/batch.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Batch < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Batch # :nodoc: 5 | 6 | # Create a Batch. 7 | def create(params = {}) 8 | wrapped_params = { batch: params } 9 | response = @client.make_request(:post, 'batches', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | def all(params = {}) 15 | filters = { key: 'batches' } 16 | 17 | get_all_helper('batches', MODEL_CLASS, params, filters) 18 | end 19 | 20 | # Retrieve a Batch 21 | def retrieve(id) 22 | response = @client.make_request(:get, "batches/#{id}") 23 | 24 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 25 | end 26 | 27 | # Buy a Batch. 28 | def buy(id, params = {}) 29 | response = @client.make_request(:post, "batches/#{id}/buy", params) 30 | 31 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 32 | end 33 | 34 | # Convert the label format of a Batch. 35 | def label(id, params = {}) 36 | response = @client.make_request(:post, "batches/#{id}/label", params) 37 | 38 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 39 | end 40 | 41 | # Remove Shipments from a Batch. 42 | def remove_shipments(id, params = {}) 43 | response = @client.make_request(:post, "batches/#{id}/remove_shipments", params) 44 | 45 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 46 | end 47 | 48 | # Add Shipments to a Batch. 49 | def add_shipments(id, params = {}) 50 | response = @client.make_request(:post, "batches/#{id}/add_shipments", params) 51 | 52 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 53 | end 54 | 55 | # Create a ScanForm for a Batch. 56 | def create_scan_form(id, params = {}) 57 | response = @client.make_request(:post, "batches/#{id}/scan_form", params) 58 | 59 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 60 | end 61 | end 62 | -------------------------------------------------------------------------------- /lib/easypost/services/beta_rate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::BetaRate < EasyPost::Services::Service 4 | # Retrieve a list of stateless rates. 5 | def retrieve_stateless_rates(params = {}) 6 | wrapped_params = { 7 | shipment: params, 8 | } 9 | response = @client.make_request(:post, 'rates', wrapped_params, 'beta') 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::Rate).rates 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /lib/easypost/services/beta_referral_customer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::BetaReferralCustomer < EasyPost::Services::Service 4 | # Add a Stripe payment method to a ReferralCustomer Customer. This function requires the ReferralCustomer Customer's API key. 5 | def add_payment_method(stripe_customer_id, payment_method_reference, priority = 'primary') 6 | wrapped_params = { 7 | payment_method: { 8 | stripe_customer_id: stripe_customer_id, 9 | payment_method_reference: payment_method_reference, 10 | priority: priority.downcase, 11 | }, 12 | } 13 | response = @client.make_request( 14 | :post, 15 | 'referral_customers/payment_method', 16 | wrapped_params, 17 | 'beta', 18 | ) 19 | 20 | EasyPost::InternalUtilities::Json.convert_json_to_object(response) 21 | end 22 | 23 | # Refund a ReferralCustomer Customer's wallet by a specified amount. Refund will be issued to the user's original payment method. 24 | # This function requires the ReferralCustomer Customer's API key. 25 | def refund_by_amount(amount) 26 | params = { 27 | refund_amount: amount, 28 | } 29 | response = @client.make_request(:post, 'referral_customers/refunds', params, 'beta') 30 | 31 | EasyPost::InternalUtilities::Json.convert_json_to_object(response) 32 | end 33 | 34 | # Refund a ReferralCustomer Customer's wallet for a specified payment log entry. Refund will be issued to the user's original payment method. 35 | # This function requires the ReferralCustomer Customer's API key. 36 | def refund_by_payment_log(payment_log_id) 37 | params = { 38 | payment_log_id: payment_log_id, 39 | } 40 | response = @client.make_request(:post, 'referral_customers/refunds', params, 'beta') 41 | 42 | EasyPost::InternalUtilities::Json.convert_json_to_object(response) 43 | end 44 | 45 | # Creates a client secret to use with Stripe when adding a credit card. 46 | def create_credit_card_client_secret 47 | response = @client.make_request(:post, 'setup_intents', nil, 'beta') 48 | 49 | EasyPost::InternalUtilities::Json.convert_json_to_object(response) 50 | end 51 | 52 | # Creates a client secret to use with Stripe when adding a bank account. 53 | def create_bank_account_client_secret(return_url = nil) 54 | params = return_url ? { return_url: return_url } : nil 55 | response = @client.make_request(:post, 'financial_connections_sessions', params, 'beta') 56 | 57 | EasyPost::InternalUtilities::Json.convert_json_to_object(response) 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/easypost/services/billing.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'easypost/constants' 4 | 5 | class EasyPost::Services::Billing < EasyPost::Services::Service 6 | # Fund your EasyPost wallet by charging your primary or secondary card on file. 7 | def fund_wallet(amount, priority = 'primary') 8 | payment_info = get_payment_method_info(priority.downcase) 9 | endpoint = payment_info[0] 10 | payment_id = payment_info[1] 11 | 12 | wrapped_params = { amount: amount } 13 | @client.make_request(:post, "#{endpoint}/#{payment_id}/charges", wrapped_params) 14 | 15 | # Return true if succeeds, an error will be thrown if it fails 16 | true 17 | end 18 | 19 | # Delete a payment method. 20 | def delete_payment_method(priority) 21 | payment_info = get_payment_method_info(priority.downcase) 22 | endpoint = payment_info[0] 23 | payment_id = payment_info[1] 24 | 25 | @client.make_request(:delete, "#{endpoint}/#{payment_id}") 26 | 27 | # Return true if succeeds, an error will be thrown if it fails 28 | true 29 | end 30 | 31 | # Retrieve all payment methods. 32 | def retrieve_payment_methods 33 | response = @client.make_request(:get, '/payment_methods') 34 | payment_methods = EasyPost::InternalUtilities::Json.convert_json_to_object(response) 35 | 36 | if payment_methods['id'].nil? 37 | raise EasyPost::Errors::InvalidObjectError.new(EasyPost::Constants::NO_PAYMENT_METHODS) 38 | end 39 | 40 | payment_methods 41 | end 42 | 43 | private 44 | 45 | # Get payment method info (type of the payment method and ID of the payment method) 46 | def get_payment_method_info(priority) 47 | payment_methods = retrieve_payment_methods 48 | payment_method_map = { 49 | 'primary' => 'primary_payment_method', 50 | 'secondary' => 'secondary_payment_method', 51 | } 52 | 53 | payment_method_to_use = payment_method_map[priority] 54 | 55 | error_string = EasyPost::Constants::INVALID_PAYMENT_METHOD 56 | suggestion = "Please use a valid payment method: #{payment_method_map.keys.join(', ')}" 57 | if payment_methods[payment_method_to_use].nil? 58 | raise EasyPost::Errors::InvalidParameterError.new( 59 | error_string, 60 | suggestion, 61 | ) 62 | end 63 | 64 | payment_method_id = payment_methods[payment_method_to_use]['id'] 65 | payment_method_object_type = payment_methods[payment_method_to_use]['object'] 66 | 67 | if payment_method_object_type == 'CreditCard' 68 | 69 | endpoint = '/credit_cards' 70 | elsif payment_method_object_type == 'BankAccount' 71 | endpoint = '/bank_accounts' 72 | else 73 | raise EasyPost::Errors::InvalidObjectError.new(error_string) 74 | end 75 | 76 | [endpoint, payment_method_id] 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /lib/easypost/services/carrier_account.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::CarrierAccount < EasyPost::Services::Service 4 | CUSTOM_WORKFLOW_CARRIER_TYPES = %w[FedexAccount FedexSmartpostAccount].freeze 5 | CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_OAUTH = %w[ 6 | AmazonShippingAccount UpsAccount UpsMailInnovationsAccount 7 | UpsSurepostAccount 8 | ].freeze 9 | MODEL_CLASS = EasyPost::Models::CarrierAccount # :nodoc: 10 | 11 | # Create a carrier account 12 | def create(params = {}) 13 | carrier_account_type = params[:type] 14 | wrapped_params = { select_top_layer_key(carrier_account_type).to_sym => params } 15 | 16 | # For UPS and FedEx the endpoint is different 17 | create_url = if CUSTOM_WORKFLOW_CARRIER_TYPES.include?(carrier_account_type) 18 | 'carrier_accounts/register' 19 | elsif CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_OAUTH.include?(carrier_account_type) 20 | 'carrier_accounts/register_oauth' 21 | else 22 | 'carrier_accounts' 23 | end 24 | response = @client.make_request(:post, create_url, wrapped_params) 25 | 26 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 27 | end 28 | 29 | # Retrieve a carrier account 30 | def retrieve(id) 31 | response = @client.make_request(:get, "carrier_accounts/#{id}") 32 | 33 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 34 | end 35 | 36 | # Retrieve all carrier accounts 37 | def all(params = {}) 38 | get_all_helper('carrier_accounts', MODEL_CLASS, params) 39 | end 40 | 41 | # Update a carrier account 42 | def update(id, params = {}) 43 | carrier_account = retrieve(id) 44 | wrapped_params = { select_top_layer_key(carrier_account[:type]).to_sym => params } 45 | response = @client.make_request(:put, "carrier_accounts/#{id}", wrapped_params) 46 | 47 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 48 | end 49 | 50 | # Delete a carrier account 51 | def delete(id) 52 | @client.make_request(:delete, "carrier_accounts/#{id}") 53 | 54 | # Return true if succeeds, an error will be thrown if it fails 55 | true 56 | end 57 | 58 | private 59 | 60 | # Select the top-layer key for the carrier account creation/update request based on the carrier type. 61 | def select_top_layer_key(carrier_account_type) 62 | if CARRIER_ACCOUNT_TYPES_WITH_CUSTOM_OAUTH.include?(carrier_account_type) 63 | 'carrier_account_oauth_registrations' 64 | else 65 | 'carrier_account' 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /lib/easypost/services/carrier_metadata.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::CarrierMetadata < EasyPost::Services::Service 4 | # Retrieve metadata for carrier(s). 5 | def retrieve(carriers = [], types = []) 6 | path = '/metadata/carriers?' 7 | params = {} 8 | 9 | if carriers.length.positive? 10 | params[:carriers] = carriers.join(',') 11 | end 12 | 13 | if types.length.positive? 14 | params[:types] = types.join(',') 15 | end 16 | 17 | path += URI.encode_www_form(params) 18 | response = @client.make_request(:get, path, params) 19 | 20 | EasyPost::InternalUtilities::Json.convert_json_to_object(response).carriers 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /lib/easypost/services/carrier_type.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::CarrierType < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::CarrierType # :nodoc: 5 | 6 | # Retrieve all carrier types 7 | def all 8 | response = @client.make_request(:get, 'carrier_types') 9 | 10 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /lib/easypost/services/claim.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Claim < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Claim # :nodoc: 5 | 6 | # Create an Claim object 7 | def create(params = {}) 8 | response = @client.make_request(:post, 'claims', params) 9 | 10 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 11 | end 12 | 13 | # Retrieve an Claim object 14 | def retrieve(id) 15 | response = @client.make_request(:get, "claims/#{id}", nil) 16 | 17 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 18 | end 19 | 20 | # Retrieve all Claim objects 21 | def all(params = {}) 22 | filters = { key: 'claims' } 23 | 24 | get_all_helper('claims', MODEL_CLASS, params, filters) 25 | end 26 | 27 | # Get the next page of claims. 28 | def get_next_page(collection, page_size = nil) 29 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 30 | 31 | params = { before_id: collection.claims.last.id } 32 | params[:page_size] = page_size unless page_size.nil? 33 | 34 | all(params) 35 | end 36 | 37 | # Cancel a filed claim 38 | def cancel(id) 39 | response = @client.make_request(:post, "claims/#{id}/cancel", nil) 40 | 41 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /lib/easypost/services/customs_info.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::CustomsInfo < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::CustomsInfo # :nodoc: 5 | 6 | # Create a CustomsInfo object 7 | def create(params) 8 | wrapped_params = { customs_info: params } 9 | response = @client.make_request(:post, 'customs_infos', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve a CustomsInfo object 15 | def retrieve(id) 16 | response = @client.make_request(:get, "customs_infos/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/easypost/services/customs_item.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::CustomsItem < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::CustomsItem # :nodoc: 5 | 6 | # Create a CustomsItem object 7 | def create(params) 8 | response = @client.make_request(:post, 'customs_items', params) 9 | 10 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 11 | end 12 | 13 | # Retrieve a CustomsItem object 14 | def retrieve(id) 15 | response = @client.make_request(:get, "customs_items/#{id}") 16 | 17 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/easypost/services/end_shipper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::EndShipper < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::EndShipper # :nodoc: 5 | 6 | # Create an EndShipper object. 7 | def create(params = {}) 8 | wrapped_params = { address: params } 9 | response = @client.make_request(:post, 'end_shippers', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve an EndShipper object. 15 | def retrieve(id) 16 | response = @client.make_request(:get, "end_shippers/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | 21 | # Retrieve all EndShipper objects. 22 | def all(params = {}) 23 | response = @client.make_request(:get, 'end_shippers', params) 24 | 25 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 26 | end 27 | 28 | # Updates an EndShipper object. This requires all parameters to be set. 29 | def update(id, params) 30 | wrapped_params = { address: params } 31 | response = @client.make_request(:put, "end_shippers/#{id}", wrapped_params) 32 | 33 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 34 | end 35 | 36 | # TODO: Add support for getting the next page of end shippers when the API supports it. 37 | end 38 | -------------------------------------------------------------------------------- /lib/easypost/services/event.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'json' 4 | 5 | class EasyPost::Services::Event < EasyPost::Services::Service 6 | MODEL_CLASS = EasyPost::Models::Event # :nodoc: 7 | 8 | # Retrieve an Event object 9 | def retrieve(id) 10 | response = @client.make_request(:get, "events/#{id}") 11 | 12 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 13 | end 14 | 15 | # Retrieve all Event objects 16 | def all(params = {}) 17 | filters = { key: 'events' } 18 | 19 | get_all_helper('events', MODEL_CLASS, params, filters) 20 | end 21 | 22 | # Retrieve all payloads for an event. 23 | def retrieve_all_payloads(event_id) 24 | response = @client.make_request(:get, "events/#{event_id}/payloads") 25 | 26 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::Payload) 27 | end 28 | 29 | # Retrieve a specific payload for an event. 30 | def retrieve_payload(event_id, payload_id) 31 | response = @client.make_request(:get, "events/#{event_id}/payloads/#{payload_id}") 32 | 33 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::Payload) 34 | end 35 | 36 | # Get the next page of events. 37 | def get_next_page(collection, page_size = nil) 38 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 39 | 40 | params = { before_id: collection.events.last.id } 41 | params[:page_size] = page_size unless page_size.nil? 42 | 43 | all(params) 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/easypost/services/insurance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Insurance < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Insurance # :nodoc: 5 | 6 | # Create an Insurance object 7 | def create(params = {}) 8 | wrapped_params = { insurance: params } 9 | response = @client.make_request(:post, 'insurances', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve an Insurance object 15 | def retrieve(id) 16 | response = @client.make_request(:get, "insurances/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | 21 | # Retrieve all Insurance objects 22 | def all(params = {}) 23 | filters = { key: 'insurances' } 24 | 25 | get_all_helper('insurances', MODEL_CLASS, params, filters) 26 | end 27 | 28 | # Get the next page of insurances. 29 | def get_next_page(collection, page_size = nil) 30 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 31 | 32 | params = { before_id: collection.insurances.last.id } 33 | params[:page_size] = page_size unless page_size.nil? 34 | 35 | all(params) 36 | end 37 | 38 | # Refund an Insurance object 39 | def refund(id) 40 | response = @client.make_request(:post, "insurances/#{id}/refund") 41 | 42 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /lib/easypost/services/order.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Order < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Order # :nodoc: 5 | 6 | # Create an Order object 7 | def create(params = {}) 8 | wrapped_params = { order: params } 9 | response = @client.make_request(:post, 'orders', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve an Order object 15 | def retrieve(id) 16 | response = @client.make_request(:get, "orders/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | 21 | # Retrieve new rates for an Order object 22 | def get_rates(id, params = {}) 23 | response = @client.make_request(:get, "orders/#{id}/rates", params) 24 | 25 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 26 | end 27 | 28 | # Buy an Order object 29 | def buy(id, params = {}) 30 | if params.instance_of?(EasyPost::Models::Rate) 31 | params = { carrier: params[:carrier], service: params[:service] } 32 | end 33 | 34 | response = @client.make_request(:post, "orders/#{id}/buy", params) 35 | 36 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /lib/easypost/services/parcel.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Parcel < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Parcel # :nodoc: 5 | 6 | # Create a Parcel object 7 | def create(params = {}) 8 | wrapped_params = { parcel: params } 9 | response = @client.make_request(:post, 'parcels', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve a Parcel object 15 | def retrieve(id) 16 | response = @client.make_request(:get, "parcels/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /lib/easypost/services/pickup.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Pickup < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Pickup # :nodoc: 5 | 6 | # Create a Pickup object 7 | def create(params = {}) 8 | wrapped_params = { pickup: params } 9 | response = @client.make_request(:post, 'pickups', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve a Pickup object 15 | def retrieve(id) 16 | response = @client.make_request(:get, "pickups/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | 21 | # Retrieve all Pickup objects 22 | def all(params = {}) 23 | filters = { key: 'pickups' } 24 | 25 | get_all_helper('pickups', MODEL_CLASS, params, filters) 26 | end 27 | 28 | # Buy a Pickup 29 | def buy(id, params = {}) 30 | if params.instance_of?(EasyPost::Models::PickupRate) 31 | params = { carrier: params[:carrier], service: params[:service] } 32 | end 33 | 34 | response = @client.make_request(:post, "pickups/#{id}/buy", params) 35 | 36 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 37 | end 38 | 39 | # Cancel a Pickup 40 | def cancel(id, params = {}) 41 | response = @client.make_request(:post, "pickups/#{id}/cancel", params) 42 | 43 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 44 | end 45 | 46 | # Get next page of Pickups 47 | def get_next_page(collection, page_size = nil) 48 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 49 | 50 | params = { before_id: collection.pickups.last.id } 51 | params[:page_size] = page_size unless page_size.nil? 52 | 53 | all(params) 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /lib/easypost/services/rate.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Rate < EasyPost::Services::Service 4 | # Retrieve a Rate 5 | def retrieve(id) 6 | response = @client.make_request(:get, "rates/#{id}") 7 | 8 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::Rate) 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /lib/easypost/services/refund.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Refund < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Refund # :nodoc: 5 | 6 | # Create a Refund object 7 | def create(params = {}) 8 | wrapped_params = { refund: params } 9 | response = @client.make_request(:post, 'refunds', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve a Refund object 15 | def retrieve(id) 16 | response = @client.make_request(:get, "refunds/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | 21 | # Retrieve all Refund objects 22 | def all(params = {}) 23 | filters = { key: 'refunds' } 24 | 25 | get_all_helper('refunds', MODEL_CLASS, params, filters) 26 | end 27 | 28 | # Get the next page of refunds 29 | def get_next_page(collection, page_size = nil) 30 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 31 | 32 | params = { before_id: collection.refunds.last.id } 33 | params[:page_size] = page_size unless page_size.nil? 34 | 35 | all(params) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/easypost/services/report.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Report < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Report # :nodoc: 5 | 6 | # Create a Report 7 | def create(params = {}) 8 | unless params[:type] 9 | raise EasyPost::Errors::MISSING_REQUIRED_PARAMETER.new('type') 10 | end 11 | 12 | type = params.delete(:type) 13 | url = "reports/#{type}" 14 | response = @client.make_request(:post, url, params) 15 | 16 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 17 | end 18 | 19 | # Retrieve a Report 20 | def retrieve(id) 21 | response = @client.make_request(:get, "reports/#{id}") 22 | 23 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 24 | end 25 | 26 | # Retrieve all Report objects 27 | def all(params = {}) 28 | unless params[:type] 29 | raise EasyPost::Errors::MISSING_REQUIRED_PARAMETER.new('type') 30 | end 31 | 32 | type = params.delete(:type) 33 | filters = { 34 | key: 'reports', 35 | type: type, 36 | } 37 | url = "reports/#{type}" 38 | response = get_all_helper(url, MODEL_CLASS, params, filters) 39 | response.define_singleton_method(:type) { type } 40 | 41 | response 42 | end 43 | 44 | # Get next page of Report objects 45 | def get_next_page(collection, page_size = nil) 46 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 47 | 48 | params = { before_id: collection.reports.last.id } 49 | params[:page_size] = page_size unless page_size.nil? 50 | params.merge!(collection[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).delete(:key) 51 | 52 | all(params) 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /lib/easypost/services/scan_form.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::ScanForm < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::ScanForm # :nodoc: 5 | 6 | # Create a ScanForm. 7 | def create(params = {}) 8 | response = @client.make_request(:post, 'scan_forms', params) 9 | 10 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 11 | end 12 | 13 | # Retrieve a ScanForm. 14 | def retrieve(id) 15 | response = @client.make_request(:get, "scan_forms/#{id}") 16 | 17 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 18 | end 19 | 20 | # Retrieve a list of ScanForms 21 | def all(params = {}) 22 | filters = { key: 'scan_forms' } 23 | 24 | get_all_helper('scan_forms', MODEL_CLASS, params, filters) 25 | end 26 | 27 | # Get the next page of ScanForms. 28 | def get_next_page(collection, page_size = nil) 29 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 30 | 31 | params = { before_id: collection.scan_forms.last.id } 32 | params[:page_size] = page_size unless page_size.nil? 33 | 34 | all(params) 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/easypost/services/smart_rate.rb: -------------------------------------------------------------------------------- 1 | class EasyPost::Services::SmartRate < EasyPost::Services::Service 2 | # Retrieve the estimated delivery date of each carrier-service level combination via the 3 | # Smart Deliver By API, based on a specific ship date and origin-destination postal code pair. 4 | def estimate_delivery_date(params = {}) 5 | url = 'smartrate/deliver_by' 6 | 7 | response = @client.make_request(:post, url, params) 8 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::EasyPostObject) 9 | end 10 | 11 | # Retrieve a recommended ship date for each carrier-service level combination via the 12 | # Smart Deliver On API, based on a specific delivery date and origin-destination postal code pair. 13 | def recommend_ship_date(params = {}) 14 | url = 'smartrate/deliver_on' 15 | 16 | response = @client.make_request(:post, url, params) 17 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::EasyPostObject) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /lib/easypost/services/tracker.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Tracker < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Tracker # :nodoc: 5 | 6 | # Create a Tracker 7 | def create(params = {}) 8 | wrapped_params = { tracker: params } 9 | response = @client.make_request(:post, 'trackers', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve a Tracker 15 | def retrieve(id) 16 | response = @client.make_request(:get, "trackers/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | 21 | # Retrieve a list of Trackers 22 | def all(params = {}) 23 | filters = { 24 | key: 'trackers', 25 | tracking_code: params[:tracking_code], 26 | carrier: params[:carrier], 27 | } 28 | 29 | get_all_helper('trackers', MODEL_CLASS, params, filters) 30 | end 31 | 32 | # Get the next page of trackers. 33 | def get_next_page(collection, page_size = nil) 34 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 35 | 36 | params = { before_id: collection.trackers.last.id } 37 | params[:page_size] = page_size unless page_size.nil? 38 | params.merge!(collection[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).delete(:key) 39 | 40 | all(params) 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/easypost/services/user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::User < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::User # :nodoc: 5 | 6 | # Create a child User. 7 | def create(params = {}) 8 | response = @client.make_request(:post, 'users', params) 9 | 10 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 11 | end 12 | 13 | # Retrieve a user 14 | def retrieve(id) 15 | response = @client.make_request(:get, "users/#{id}") 16 | 17 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 18 | end 19 | 20 | # Retrieve the authenticated User. 21 | def retrieve_me 22 | response = @client.make_request(:get, 'users') 23 | 24 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 25 | end 26 | 27 | # Update a User 28 | def update(id, params = {}) 29 | response = @client.make_request(:put, "users/#{id}", params) 30 | 31 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 32 | end 33 | 34 | # Delete a User 35 | def delete(id) 36 | @client.make_request(:delete, "users/#{id}") 37 | 38 | # Return true if succeeds, an error will be thrown if it fails 39 | true 40 | end 41 | 42 | # Update the Brand of a User. 43 | def update_brand(id, params = {}) 44 | wrapped_params = { brand: params } 45 | response = @client.make_request(:patch, "users/#{id}/brand", wrapped_params) 46 | 47 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, EasyPost::Models::Brand) 48 | end 49 | 50 | # Retrieve all child Users. 51 | def all_children(params = {}) 52 | filters = { key: 'children' } 53 | 54 | get_all_helper('users/children', EasyPost::Models::User, params, filters) 55 | end 56 | 57 | # Get the next page of child users. 58 | def get_next_page_of_children(collection, page_size = nil) 59 | raise EasyPost::Errors::EndOfPaginationError.new unless more_pages?(collection) 60 | 61 | params = { after_id: collection.children.last.id } 62 | params[:page_size] = page_size unless page_size.nil? 63 | 64 | all_children(params) 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /lib/easypost/services/webhook.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | class EasyPost::Services::Webhook < EasyPost::Services::Service 4 | MODEL_CLASS = EasyPost::Models::Webhook # :nodoc: 5 | 6 | # Create a Webhook. 7 | def create(params = {}) 8 | wrapped_params = { webhook: params } 9 | response = @client.make_request(:post, 'webhooks', wrapped_params) 10 | 11 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 12 | end 13 | 14 | # Retrieve a Webhook 15 | def retrieve(id) 16 | response = @client.make_request(:get, "webhooks/#{id}") 17 | 18 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 19 | end 20 | 21 | # Retrieve a list of Webhooks 22 | def all(params = {}) 23 | filters = { 'key' => 'webhooks' } 24 | 25 | get_all_helper('webhooks', MODEL_CLASS, params, filters) 26 | end 27 | 28 | # Update a Webhook. 29 | def update(id, params = {}) 30 | response = @client.make_request(:patch, "webhooks/#{id}", params) 31 | 32 | EasyPost::InternalUtilities::Json.convert_json_to_object(response, MODEL_CLASS) 33 | end 34 | 35 | # Delete a Webhook. 36 | def delete(id) 37 | @client.make_request(:delete, "webhooks/#{id}") 38 | 39 | # Return true if succeeds, an error will be thrown if it fails 40 | true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /lib/easypost/utilities/constants.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::InternalUtilities::Constants 4 | API_VERSION = 'v2' 5 | FILTERS_KEY = '_filters' 6 | end 7 | -------------------------------------------------------------------------------- /lib/easypost/utilities/json.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::InternalUtilities::Json 4 | def self.convert_json_to_object(data, cls = EasyPost::Models::EasyPostObject) 5 | data = parse_json(data) if data.is_a?(String) # Parse JSON to a Hash or Array if it's a string 6 | if data.is_a?(Array) 7 | # Deserialize array data into an array of objects 8 | data.map { |i| convert_json_to_object(i, cls) } 9 | elsif data.is_a?(Hash) 10 | # Deserialize hash data into a new object instance 11 | cls.new(data) 12 | else 13 | # data is neither a Hash nor Array (but somehow was parsed as JSON? This should never happen) 14 | data 15 | end 16 | end 17 | 18 | def self.parse_json(data) 19 | return if data.nil? 20 | 21 | JSON.parse(data) 22 | rescue JSON::ParserError 23 | data # Not JSON, return the original data (used mostly when dealing with final values like strings, booleans, etc.) 24 | end 25 | 26 | def self.http_response_is_json?(response) 27 | response['Content-Type'] ? response['Content-Type'].start_with?('application/json') : false 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /lib/easypost/utilities/static_mapper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../models' 4 | 5 | module EasyPost::InternalUtilities::StaticMapper 6 | BY_PREFIX = { 7 | 'adr' => EasyPost::Models::Address, 8 | 'ak' => EasyPost::Models::ApiKey, 9 | 'bank' => EasyPost::Models::PaymentMethod, 10 | 'batch' => EasyPost::Models::Batch, 11 | 'brd' => EasyPost::Models::Brand, 12 | 'ca' => EasyPost::Models::CarrierAccount, 13 | 'card' => EasyPost::Models::PaymentMethod, 14 | 'clm' => EasyPost::Models::Claim, 15 | 'cstinfo' => EasyPost::Models::CustomsInfo, 16 | 'cstitem' => EasyPost::Models::CustomsItem, 17 | 'es' => EasyPost::Models::EndShipper, 18 | 'evt' => EasyPost::Models::Event, 19 | 'hook' => EasyPost::Models::Webhook, 20 | 'ins' => EasyPost::Models::Insurance, 21 | 'order' => EasyPost::Models::Order, 22 | 'payload' => EasyPost::Models::Payload, 23 | 'pickup' => EasyPost::Models::Pickup, 24 | 'pickuprate' => EasyPost::Models::PickupRate, 25 | 'pl' => EasyPost::Models::PostageLabel, 26 | 'plrep' => EasyPost::Models::Report, 27 | 'prcl' => EasyPost::Models::Parcel, 28 | 'rate' => EasyPost::Models::Rate, 29 | 'refrep' => EasyPost::Models::Report, 30 | 'rfnd' => EasyPost::Models::Refund, 31 | 'sf' => EasyPost::Models::ScanForm, 32 | 'shp' => EasyPost::Models::Shipment, 33 | 'shpinvrep' => EasyPost::Models::Report, 34 | 'shprep' => EasyPost::Models::Report, 35 | 'trk' => EasyPost::Models::Tracker, 36 | 'trkrep' => EasyPost::Models::Report, 37 | 'user' => EasyPost::Models::User, 38 | }.freeze 39 | 40 | BY_TYPE = { 41 | 'Address' => EasyPost::Models::Address, 42 | 'ApiKey' => EasyPost::Models::ApiKey, 43 | 'BankAccount' => EasyPost::Models::PaymentMethod, 44 | 'Batch' => EasyPost::Models::Batch, 45 | 'Brand' => EasyPost::Models::Brand, 46 | 'CarrierAccount' => EasyPost::Models::CarrierAccount, 47 | 'Claim' => EasyPost::Models::Claim, 48 | 'CreditCard' => EasyPost::Models::PaymentMethod, 49 | 'CustomsInfo' => EasyPost::Models::CustomsInfo, 50 | 'CustomsItem' => EasyPost::Models::CustomsItem, 51 | 'EndShipper' => EasyPost::Models::EndShipper, 52 | 'Event' => EasyPost::Models::Event, 53 | 'Insurance' => EasyPost::Models::Insurance, 54 | 'Order' => EasyPost::Models::Order, 55 | 'Parcel' => EasyPost::Models::Parcel, 56 | 'PaymentLogReport' => EasyPost::Models::Report, 57 | 'Pickup' => EasyPost::Models::Pickup, 58 | 'PickupRate' => EasyPost::Models::PickupRate, 59 | 'PostageLabel' => EasyPost::Models::PostageLabel, 60 | 'Rate' => EasyPost::Models::Rate, 61 | 'Refund' => EasyPost::Models::Refund, 62 | 'RefundReport' => EasyPost::Models::Report, 63 | 'Report' => EasyPost::Models::Report, 64 | 'ScanForm' => EasyPost::Models::ScanForm, 65 | 'Shipment' => EasyPost::Models::Shipment, 66 | 'ShipmentInvoiceReport' => EasyPost::Models::Report, 67 | 'ShipmentReport' => EasyPost::Models::Report, 68 | 'TaxIdentifier' => EasyPost::Models::TaxIdentifier, 69 | 'Tracker' => EasyPost::Models::Tracker, 70 | 'TrackerReport' => EasyPost::Models::Report, 71 | 'User' => EasyPost::Models::User, 72 | 'Webhook' => EasyPost::Models::Webhook, 73 | }.freeze 74 | end 75 | -------------------------------------------------------------------------------- /lib/easypost/utilities/system.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost::InternalUtilities::System 4 | def self.os_name 5 | case RUBY_PLATFORM 6 | when /linux/i 7 | 'Linux' 8 | when /darwin/i 9 | 'Darwin' 10 | when /cygwin|mswin|mingw|bccwin|wince|emx/i 11 | 'Windows' 12 | else 13 | 'Unknown' 14 | end 15 | end 16 | 17 | def self.os_version 18 | Gem::Platform.local.version 19 | end 20 | 21 | def self.os_arch 22 | Gem::Platform.local.cpu 23 | end 24 | 25 | def self.ruby_version 26 | RUBY_VERSION 27 | end 28 | 29 | def self.ruby_patchlevel 30 | RUBY_PATCHLEVEL 31 | end 32 | 33 | def self.lib_version 34 | File.open(File.expand_path('../../VERSION', __dir__)).read.strip 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/easypost/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module EasyPost 4 | VERSION = File.open(File.expand_path('../../VERSION', __dir__)).read.strip 5 | end 6 | -------------------------------------------------------------------------------- /spec/api_key_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::ApiKey, :authenticate_prod do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_PROD_API_KEY']) } 7 | 8 | describe '.all' do 9 | it 'retrieves all API keys' do 10 | api_keys = client.api_key.all 11 | 12 | expect(api_keys.keys).to all(be_an_instance_of(EasyPost::Models::ApiKey)) 13 | api_keys.children.each do |child| 14 | expect(child.keys).to all(be_an_instance_of(EasyPost::Models::ApiKey)) 15 | end 16 | end 17 | end 18 | 19 | describe '.retrieve_api_keys_for_user' do 20 | it "retrieves the authenticated user's API keys" do 21 | user = client.user.retrieve_me 22 | api_keys = client.api_key.retrieve_api_keys_for_user(user.id) 23 | 24 | expect(api_keys).to all(be_an_instance_of(EasyPost::Models::ApiKey)) 25 | end 26 | 27 | it "retrieves child user's API keys as a parent" do 28 | user = client.user.create( 29 | name: 'Test User', 30 | ) 31 | child_user = client.user.retrieve(user.id) 32 | 33 | api_keys = client.api_key.retrieve_api_keys_for_user(child_user.id) 34 | 35 | expect(api_keys).to all(be_an_instance_of(EasyPost::Models::ApiKey)) 36 | 37 | client.user.delete(user.id) # delete the user so we don't clutter the test environment 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/beta_rate_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::BetaRate do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.retrieve_stateless_rates' do 9 | it 'retrieve all stateless rates' do 10 | stateless_rates = client.beta_rate.retrieve_stateless_rates(Fixture.basic_shipment) 11 | 12 | expect(stateless_rates).to all(be_an_instance_of(EasyPost::Models::Rate)) 13 | end 14 | 15 | describe '.get_lowest_stateless_rate' do 16 | it 'gets the lowest stateless rate for various combinations of filters' do 17 | stateless_rates = client.beta_rate.retrieve_stateless_rates(Fixture.basic_shipment) 18 | 19 | lowest_stateless_rate = EasyPost::Util.get_lowest_stateless_rate(stateless_rates) 20 | 21 | expect(lowest_stateless_rate.service).to match('GroundAdvantage') 22 | 23 | expect { 24 | EasyPost::Util.get_lowest_stateless_rate(stateless_rates, ['invalid_carrier']) 25 | }.to raise_error(EasyPost::Errors::FilteringError, EasyPost::Constants::NO_MATCHING_RATES) 26 | 27 | expect { 28 | EasyPost::Util.get_lowest_stateless_rate(stateless_rates, [], ['invalid_service']) 29 | }.to raise_error(EasyPost::Errors::FilteringError, EasyPost::Constants::NO_MATCHING_RATES) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/beta_referral_customer_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::BetaReferralCustomer do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['PARTNER_USER_PROD_API_KEY'] || '123') } 7 | 8 | describe '.add_payment_method' do 9 | it 'adds a Stripe card or bank account to a referral customer account' do 10 | expect { 11 | client.beta_referral_customer.add_payment_method( 12 | 'cus_123', 13 | 'ba_123', 14 | ) 15 | }.to raise_error(EasyPost::Errors::EasyPostError).with_message('Invalid Payment Gateway Reference.') 16 | end 17 | end 18 | 19 | describe '.refund_by_amount' do 20 | it 'refunds a referral user by a specific amount' do 21 | expect { 22 | client.beta_referral_customer.refund_by_amount( 23 | 2000, 24 | ) 25 | }.to raise_error(EasyPost::Errors::EasyPostError).with_message( 26 | 'Refund amount is invalid. Please use a valid amount or escalate to finance.', 27 | ) 28 | end 29 | end 30 | 31 | describe '.refund_by_payment_log' do 32 | it 'refunds a referral user by a specific payment log entry' do 33 | expect { 34 | client.beta_referral_customer.refund_by_payment_log( 35 | 'paylog_123', 36 | ) 37 | }.to raise_error(EasyPost::Errors::EasyPostError).with_message( 38 | 'We could not find a transaction with that id.', 39 | ) 40 | end 41 | end 42 | 43 | describe '.create_credit_card_client_secret' do 44 | it 'returns a client secret for credit cards' do 45 | response = client.beta_referral_customer.create_credit_card_client_secret 46 | 47 | expect(response.client_secret).to match('seti_') 48 | end 49 | end 50 | 51 | describe '.create_bank_account_client_secret' do 52 | it 'returns a client secret for bank accounts' do 53 | response = client.beta_referral_customer.create_bank_account_client_secret 54 | 55 | expect(response.client_secret).to match('fcsess_client_secret_') 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/carrier_metadata_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::CarrierMetadata do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.retrieve_carrier_metadata' do 9 | it 'retrieve metadata for all carriers' do 10 | metadata = client.carrier_metadata.retrieve 11 | 12 | expect(metadata).to be_an_instance_of(Array) 13 | 14 | expect(metadata.find { |carrier| carrier.name == 'usps' }).not_to be_nil 15 | expect(metadata.find { |carrier| carrier.name == 'fedex' }).not_to be_nil 16 | end 17 | 18 | it 'retrieve metadata for a single carrier' do 19 | carrier = 'usps' 20 | types = %w[service_levels predefined_packages] 21 | 22 | metadata = client.carrier_metadata.retrieve(['usps'], types) 23 | 24 | expect(metadata).to be_an_instance_of(Array) 25 | 26 | first_entry = metadata.first 27 | 28 | # Assert we only have one carrier, and it's the one we requested 29 | expect(metadata.length).to eq(1) 30 | expect(first_entry[:name]).to eq(carrier) 31 | 32 | # Assert we have the requested types 33 | expect(first_entry).to have_attributes(service_levels: Array, predefined_packages: Array) 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/carrier_type_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::CarrierType do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_PROD_API_KEY']) } 7 | 8 | describe '.all' do 9 | it 'retrieves all carrier types' do 10 | carrier_types = client.carrier_type.all 11 | 12 | expect(carrier_types).to all(be_an_instance_of(EasyPost::Models::CarrierType)) 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/cassettes/address/EasyPost_Services_Address_address_service_creates_an_address.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/addresses 6 | body: 7 | encoding: UTF-8 8 | string: '{"name":"Jack Sparrow","street1":"388 Townsend St","street2":"Apt 20","city":"San 9 | Francisco","state":"CA","zip":"94107","country":"US","email":"test@example.com","phone":"5555555555"}' 10 | headers: 11 | Accept-Encoding: 12 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 13 | Accept: 14 | - "*/*" 15 | User-Agent: "" 16 | Host: 17 | - api.easypost.com 18 | Content-Type: 19 | - application/json 20 | Authorization: "" 21 | response: 22 | status: 23 | code: 201 24 | message: Created 25 | headers: 26 | X-Frame-Options: 27 | - SAMEORIGIN 28 | X-Xss-Protection: 29 | - 1; mode=block 30 | X-Content-Type-Options: 31 | - nosniff 32 | X-Download-Options: 33 | - noopen 34 | X-Permitted-Cross-Domain-Policies: 35 | - none 36 | Referrer-Policy: 37 | - strict-origin-when-cross-origin 38 | X-Ep-Request-Uuid: 39 | - d044fa7966a7da4de799d88000596c2a 40 | Cache-Control: 41 | - private, no-cache, no-store 42 | Pragma: 43 | - no-cache 44 | Expires: 45 | - '0' 46 | Location: 47 | - "/api/v2/addresses/adr_5f7ae99a4dd511ef93b8ac1f6bc539ae" 48 | Content-Type: 49 | - application/json; charset=utf-8 50 | X-Runtime: 51 | - '0.057215' 52 | Transfer-Encoding: 53 | - chunked 54 | X-Node: 55 | - bigweb39nuq 56 | X-Version-Label: 57 | - easypost-202407291746-57ea285141-master 58 | X-Backend: 59 | - easypost 60 | X-Proxied: 61 | - extlb1nuq fa152d4755 62 | - intlb4nuq c0f5e722d1 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains; preload 65 | body: 66 | encoding: UTF-8 67 | string: '{"id":"adr_5f7ae99a4dd511ef93b8ac1f6bc539ae","object":"Address","created_at":"2024-07-29T18:07:09+00:00","updated_at":"2024-07-29T18:07:09+00:00","name":"Jack 68 | Sparrow","company":null,"street1":"388 Townsend St","street2":"Apt 20","city":"San 69 | Francisco","state":"CA","zip":"94107","country":"US","phone":"","email":"","mode":"test","carrier_facility":null,"residential":null,"federal_tax_id":null,"state_tax_id":null,"verifications":{}}' 70 | recorded_at: Mon, 29 Jul 2024 18:07:09 GMT 71 | recorded_with: VCR 6.1.0 72 | -------------------------------------------------------------------------------- /spec/cassettes/address/EasyPost_Services_Address_address_service_creates_an_address_with_verify_strict_param.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/addresses 6 | body: 7 | encoding: UTF-8 8 | string: '{"name":"Jack Sparrow","street1":"388 Townsend St","street2":"Apt 20","city":"San 9 | Francisco","state":"CA","zip":"94107","country":"US","email":"test@example.com","phone":"5555555555","verify_strict":true}' 10 | headers: 11 | Accept-Encoding: 12 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 13 | Accept: 14 | - "*/*" 15 | User-Agent: "" 16 | Host: 17 | - api.easypost.com 18 | Content-Type: 19 | - application/json 20 | Authorization: "" 21 | response: 22 | status: 23 | code: 201 24 | message: Created 25 | headers: 26 | X-Frame-Options: 27 | - SAMEORIGIN 28 | X-Xss-Protection: 29 | - 1; mode=block 30 | X-Content-Type-Options: 31 | - nosniff 32 | X-Download-Options: 33 | - noopen 34 | X-Permitted-Cross-Domain-Policies: 35 | - none 36 | Referrer-Policy: 37 | - strict-origin-when-cross-origin 38 | X-Ep-Request-Uuid: 39 | - e719e02c66a7da4fe799d89a005a6faa 40 | Cache-Control: 41 | - private, no-cache, no-store 42 | Pragma: 43 | - no-cache 44 | Expires: 45 | - '0' 46 | Location: 47 | - "/api/v2/addresses/adr_605729c14dd511ef95e7ac1f6bc539aa" 48 | Content-Type: 49 | - application/json; charset=utf-8 50 | X-Runtime: 51 | - '0.059347' 52 | Transfer-Encoding: 53 | - chunked 54 | X-Node: 55 | - bigweb38nuq 56 | X-Version-Label: 57 | - easypost-202407291746-57ea285141-master 58 | X-Backend: 59 | - easypost 60 | X-Proxied: 61 | - extlb2nuq fa152d4755 62 | - intlb3nuq c0f5e722d1 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains; preload 65 | body: 66 | encoding: UTF-8 67 | string: '{"id":"adr_605729c14dd511ef95e7ac1f6bc539aa","object":"Address","created_at":"2024-07-29T18:07:11+00:00","updated_at":"2024-07-29T18:07:11+00:00","name":"JACK 68 | SPARROW","company":null,"street1":"388 TOWNSEND ST APT 20","street2":"","city":"SAN 69 | FRANCISCO","state":"CA","zip":"94107-1670","country":"US","phone":"","email":"","mode":"test","carrier_facility":null,"residential":true,"federal_tax_id":null,"state_tax_id":null,"verifications":{"zip4":{"success":true,"errors":[],"details":null},"delivery":{"success":true,"errors":[],"details":{"latitude":37.77551,"longitude":-122.39697,"time_zone":"America/Los_Angeles"}}}}' 70 | recorded_at: Mon, 29 Jul 2024 18:07:11 GMT 71 | recorded_with: VCR 6.1.0 72 | -------------------------------------------------------------------------------- /spec/cassettes/address/EasyPost_Services_Address_create_and_verify_creates_a_verified_address.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/addresses/create_and_verify 6 | body: 7 | encoding: UTF-8 8 | string: '{"address":{"name":"Jack Sparrow","street1":"388 Townsend St","street2":"Apt 9 | 20","city":"San Francisco","state":"CA","zip":"94107","country":"US","email":"test@example.com","phone":"5555555555"}}' 10 | headers: 11 | Accept-Encoding: 12 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 13 | Accept: 14 | - "*/*" 15 | User-Agent: "" 16 | Host: 17 | - api.easypost.com 18 | Content-Type: 19 | - application/json 20 | Authorization: "" 21 | response: 22 | status: 23 | code: 200 24 | message: OK 25 | headers: 26 | X-Frame-Options: 27 | - SAMEORIGIN 28 | X-Xss-Protection: 29 | - 1; mode=block 30 | X-Content-Type-Options: 31 | - nosniff 32 | X-Download-Options: 33 | - noopen 34 | X-Permitted-Cross-Domain-Policies: 35 | - none 36 | Referrer-Policy: 37 | - strict-origin-when-cross-origin 38 | X-Ep-Request-Uuid: 39 | - d044fa7766a7da52e799d8a2005971cd 40 | Cache-Control: 41 | - private, no-cache, no-store 42 | Pragma: 43 | - no-cache 44 | Expires: 45 | - '0' 46 | Location: 47 | - "/api/v2/addresses/adr_6282f0d34dd511ef971cac1f6bc539aa" 48 | Content-Type: 49 | - application/json; charset=utf-8 50 | X-Runtime: 51 | - '0.086765' 52 | Transfer-Encoding: 53 | - chunked 54 | X-Node: 55 | - bigweb35nuq 56 | X-Version-Label: 57 | - easypost-202407291746-57ea285141-master 58 | X-Backend: 59 | - easypost 60 | X-Proxied: 61 | - extlb1nuq fa152d4755 62 | - intlb4nuq c0f5e722d1 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains; preload 65 | body: 66 | encoding: UTF-8 67 | string: '{"address":{"id":"adr_6282f0d34dd511ef971cac1f6bc539aa","object":"Address","created_at":"2024-07-29T18:07:14+00:00","updated_at":"2024-07-29T18:07:14+00:00","name":"JACK 68 | SPARROW","company":null,"street1":"388 TOWNSEND ST APT 20","street2":"","city":"SAN 69 | FRANCISCO","state":"CA","zip":"94107-1670","country":"US","phone":"","email":"","mode":"test","carrier_facility":null,"residential":true,"federal_tax_id":null,"state_tax_id":null,"verifications":{"zip4":{"success":true,"errors":[],"details":null},"delivery":{"success":true,"errors":[],"details":{"latitude":37.77551,"longitude":-122.39697,"time_zone":"America/Los_Angeles"}}}}}' 70 | recorded_at: Mon, 29 Jul 2024 18:07:14 GMT 71 | recorded_with: VCR 6.1.0 72 | -------------------------------------------------------------------------------- /spec/cassettes/address/EasyPost_Services_Address_create_and_verify_throws_an_error_for_invalid_address_verification.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/addresses/create_and_verify 6 | body: 7 | encoding: UTF-8 8 | string: '{"address":{"company":"EasyPost","street1":"000 unknown street","city":"Not 9 | A City","state":"ZZ","zip":"00001","country":"US","email":"test@example.com","phone":"5555555555"}}' 10 | headers: 11 | Accept-Encoding: 12 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 13 | Accept: 14 | - "*/*" 15 | User-Agent: "" 16 | Host: 17 | - api.easypost.com 18 | Content-Type: 19 | - application/json 20 | Authorization: "" 21 | response: 22 | status: 23 | code: 422 24 | message: Unprocessable Entity 25 | headers: 26 | X-Frame-Options: 27 | - SAMEORIGIN 28 | X-Xss-Protection: 29 | - 1; mode=block 30 | X-Content-Type-Options: 31 | - nosniff 32 | X-Download-Options: 33 | - noopen 34 | X-Permitted-Cross-Domain-Policies: 35 | - none 36 | Referrer-Policy: 37 | - strict-origin-when-cross-origin 38 | X-Ep-Request-Uuid: 39 | - d044fa7566a7da53e799d8a300597258 40 | Cache-Control: 41 | - private, no-cache, no-store 42 | Pragma: 43 | - no-cache 44 | Expires: 45 | - '0' 46 | Content-Type: 47 | - application/json; charset=utf-8 48 | X-Runtime: 49 | - '0.040685' 50 | Transfer-Encoding: 51 | - chunked 52 | X-Node: 53 | - bigweb42nuq 54 | X-Version-Label: 55 | - easypost-202407291746-57ea285141-master 56 | X-Backend: 57 | - easypost 58 | X-Proxied: 59 | - extlb1nuq fa152d4755 60 | - intlb3nuq c0f5e722d1 61 | Strict-Transport-Security: 62 | - max-age=31536000; includeSubDomains; preload 63 | body: 64 | encoding: UTF-8 65 | string: '{"error":{"code":"ADDRESS.VERIFY.FAILURE","message":"Unable to verify 66 | address.","errors":[{"code":"E.ADDRESS.NOT_FOUND","field":"address","message":"Address 67 | not found","suggestion":null}]}}' 68 | recorded_at: Mon, 29 Jul 2024 18:07:15 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/cassettes/batch/EasyPost_Services_Batch_batch_service_create_a_batch.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/batches 6 | body: 7 | encoding: UTF-8 8 | string: '{"batch":{"shipments":[{"to_address":{"name":"Jack Sparrow","street1":"388 9 | Townsend St","street2":"Apt 20","city":"San Francisco","state":"CA","zip":"94107","country":"US","email":"test@example.com","phone":"5555555555"},"from_address":{"name":"Elizabeth 10 | Swan","street1":"179 N Harbor Dr","city":"Redondo Beach","state":"CA","zip":"90277","country":"US","email":"test@example.com","phone":"5555555555"},"parcel":{"length":10,"width":8,"height":4,"weight":15.4},"service":"GroundAdvantage","carrier_accounts":["ca_716f33fd9fd348238b85c2922237f98b"],"carrier":"USPS"}]}}' 11 | headers: 12 | Accept-Encoding: 13 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 14 | Accept: 15 | - "*/*" 16 | User-Agent: "" 17 | Host: 18 | - api.easypost.com 19 | Content-Type: 20 | - application/json 21 | Authorization: "" 22 | response: 23 | status: 24 | code: 200 25 | message: OK 26 | headers: 27 | X-Frame-Options: 28 | - SAMEORIGIN 29 | X-Xss-Protection: 30 | - 1; mode=block 31 | X-Content-Type-Options: 32 | - nosniff 33 | X-Download-Options: 34 | - noopen 35 | X-Permitted-Cross-Domain-Policies: 36 | - none 37 | Referrer-Policy: 38 | - strict-origin-when-cross-origin 39 | X-Ep-Request-Uuid: 40 | - 3567503f66abe7c7e799e56600154fd4 41 | Cache-Control: 42 | - private, no-cache, no-store 43 | Pragma: 44 | - no-cache 45 | Expires: 46 | - '0' 47 | Content-Type: 48 | - application/json; charset=utf-8 49 | X-Runtime: 50 | - '0.054799' 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb39nuq 55 | X-Version-Label: 56 | - easypost-202408011906-6fc64ddcfc-master 57 | X-Backend: 58 | - easypost 59 | X-Proxied: 60 | - extlb1nuq fa152d4755 61 | - intlb4nuq c0f5e722d1 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"id":"batch_1f1d0f02355041038c5b9a9ad9660c87","object":"Batch","mode":"test","state":"creating","num_shipments":1,"reference":null,"created_at":"2024-08-01T19:53:44Z","updated_at":"2024-08-01T19:53:44Z","scan_form":null,"shipments":[],"status":{"created":0,"queued_for_purchase":0,"creation_failed":0,"postage_purchased":0,"postage_purchase_failed":0},"pickup":null,"label_url":null}' 67 | recorded_at: Thu, 01 Aug 2024 19:53:44 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/cassettes/beta_referral_customer/EasyPost_Services_BetaReferralCustomer_add_payment_method_adds_a_Stripe_card_or_bank_account_to_a_referral_customer_account.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/beta/referral_customers/payment_method 6 | body: 7 | encoding: UTF-8 8 | string: '{"payment_method":{"stripe_customer_id":"cus_123","payment_method_reference":"ba_123","priority":"primary"}}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 422 23 | message: Unprocessable Entity 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - e77cdde965b00a3fe787d5830006fe41 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.051276' 49 | Vary: 50 | - Origin 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb36nuq 55 | X-Version-Label: 56 | - easypost-202401222120-581bacedf7-master 57 | X-Backend: 58 | - easypost 59 | X-Proxied: 60 | - extlb2nuq 003ad9bca0 61 | - intlb1nuq 1a7e326d32 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"error":{"code":"BILLING.INVALID_PAYMENT_GATEWAY_REFERENCE","message":"Invalid 67 | Payment Gateway Reference.","errors":[]}}' 68 | recorded_at: Tue, 23 Jan 2024 18:49:35 GMT 69 | recorded_with: VCR 6.2.0 70 | -------------------------------------------------------------------------------- /spec/cassettes/beta_referral_customer/EasyPost_Services_BetaReferralCustomer_create_bank_account_client_secret_returns_a_client_secret_for_bank_accounts.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/beta/financial_connections_sessions 6 | body: 7 | encoding: UTF-8 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 201 23 | message: Created 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - 3371ca1b67d9d84be2b994960026ccf9 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.456924' 49 | Vary: 50 | - Origin 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb40nuq 55 | X-Version-Label: 56 | - easypost-202503181912-465deca9d7-master 57 | X-Backend: 58 | - easypost 59 | X-Proxied: 60 | - extlb1nuq 99aac35317 61 | - intlb3nuq 324e75def6 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"client_secret":"fcsess_client_secret_r3YZpT7sCvTxKLKdYv2je909"}' 67 | recorded_at: Tue, 18 Mar 2025 20:32:11 GMT 68 | recorded_with: VCR 6.3.1 69 | -------------------------------------------------------------------------------- /spec/cassettes/beta_referral_customer/EasyPost_Services_BetaReferralCustomer_create_credit_card_client_secret_returns_a_client_secret_for_credit_cards.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/beta/setup_intents 6 | body: 7 | encoding: UTF-8 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - 3371ca2067d9d81ee2b9947700269670 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.440041' 49 | Vary: 50 | - Origin 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb56nuq 55 | X-Version-Label: 56 | - easypost-202503181912-465deca9d7-master 57 | X-Backend: 58 | - easypost 59 | X-Proxied: 60 | - extlb1nuq 99aac35317 61 | - intlb3nuq 324e75def6 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"client_secret":"seti_0R46nLDqT4huGUvdj6lqijPj_secret_Ry2tozoWcnXkGvmCuRWgsSc3W3GRIOl"}' 67 | recorded_at: Tue, 18 Mar 2025 20:31:27 GMT 68 | recorded_with: VCR 6.3.1 69 | -------------------------------------------------------------------------------- /spec/cassettes/beta_referral_customer/EasyPost_Services_BetaReferralCustomer_refund_by_amount_refunds_a_referral_user_by_a_specific_amount.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/beta/referral_customers/refunds 6 | body: 7 | encoding: UTF-8 8 | string: '{"refund_amount":2000}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 422 23 | message: Unprocessable Entity 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7966a7da63e799d904005984fb 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.335595' 49 | Vary: 50 | - Origin 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb39nuq 55 | X-Version-Label: 56 | - easypost-202407291746-57ea285141-master 57 | X-Backend: 58 | - easypost 59 | X-Proxied: 60 | - extlb1nuq fa152d4755 61 | - intlb4nuq c0f5e722d1 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"error":{"code":"TRANSACTION.AMOUNT_INVALID","message":"Refund amount 67 | is invalid. Please use a valid amount or escalate to finance.","errors":[]}}' 68 | recorded_at: Mon, 29 Jul 2024 18:07:31 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/cassettes/beta_referral_customer/EasyPost_Services_BetaReferralCustomer_refund_by_payment_log_refunds_a_referral_user_by_a_specific_payment_log_entry.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/beta/referral_customers/refunds 6 | body: 7 | encoding: UTF-8 8 | string: '{"payment_log_id":"paylog_123"}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 422 23 | message: Unprocessable Entity 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7866a7da64e799d905005985e6 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.048578' 49 | Vary: 50 | - Origin 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb35nuq 55 | X-Version-Label: 56 | - easypost-202407291746-57ea285141-master 57 | X-Backend: 58 | - easypost 59 | X-Proxied: 60 | - extlb1nuq fa152d4755 61 | - intlb4nuq c0f5e722d1 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"error":{"code":"TRANSACTION.DOES_NOT_EXIST","message":"We could not 67 | find a transaction with that id.","errors":[]}}' 68 | recorded_at: Mon, 29 Jul 2024 18:07:32 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/cassettes/client/EasyPost_Client_client_object_allows_Faraday_to_be_used_as_a_custom_client.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/addresses/adr_123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Content-Type: 11 | - application/json 12 | User-Agent: "" 13 | Authorization: "" 14 | Accept-Encoding: 15 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 16 | Accept: 17 | - "*/*" 18 | response: 19 | status: 20 | code: 403 21 | message: Forbidden 22 | headers: 23 | X-Frame-Options: 24 | - SAMEORIGIN 25 | X-Xss-Protection: 26 | - 1; mode=block 27 | X-Content-Type-Options: 28 | - nosniff 29 | X-Download-Options: 30 | - noopen 31 | X-Permitted-Cross-Domain-Policies: 32 | - none 33 | Referrer-Policy: 34 | - strict-origin-when-cross-origin 35 | X-Ep-Request-Uuid: 36 | - d044fa7b66a7da7de799dc810059a0db 37 | Content-Type: 38 | - application/json; charset=utf-8 39 | X-Runtime: 40 | - '0.012826' 41 | Transfer-Encoding: 42 | - chunked 43 | X-Node: 44 | - bigweb38nuq 45 | X-Version-Label: 46 | - easypost-202407291746-57ea285141-master 47 | X-Backend: 48 | - easypost 49 | X-Proxied: 50 | - extlb1nuq fa152d4755 51 | - intlb4nuq c0f5e722d1 52 | Strict-Transport-Security: 53 | - max-age=31536000; includeSubDomains; preload 54 | body: 55 | encoding: UTF-8 56 | string: '{"error":{"code":"APIKEY.INACTIVE","message":"This api key is no longer 57 | active. Please use a different api key or reactivate this key.","errors":[]}}' 58 | recorded_at: Mon, 29 Jul 2024 18:07:57 GMT 59 | recorded_with: VCR 6.1.0 60 | -------------------------------------------------------------------------------- /spec/cassettes/client/EasyPost_Client_client_object_allows_Typhoeus_to_be_used_as_a_custom_client.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/addresses/adr_123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | User-Agent: "" 11 | Content-Type: 12 | - application/json 13 | Authorization: "" 14 | Expect: 15 | - '' 16 | response: 17 | status: 18 | code: 403 19 | message: '' 20 | headers: 21 | X-Frame-Options: 22 | - SAMEORIGIN 23 | X-Xss-Protection: 24 | - 1; mode=block 25 | X-Content-Type-Options: 26 | - nosniff 27 | X-Download-Options: 28 | - noopen 29 | X-Permitted-Cross-Domain-Policies: 30 | - none 31 | Referrer-Policy: 32 | - strict-origin-when-cross-origin 33 | X-Ep-Request-Uuid: 34 | - d044fa7566a7da7ee799dc990059a14e 35 | Content-Type: 36 | - application/json; charset=utf-8 37 | Content-Length: 38 | - '149' 39 | X-Runtime: 40 | - '0.011933' 41 | X-Node: 42 | - bigweb39nuq 43 | X-Version-Label: 44 | - easypost-202407291746-57ea285141-master 45 | X-Backend: 46 | - easypost 47 | X-Proxied: 48 | - extlb1nuq fa152d4755 49 | - intlb4nuq c0f5e722d1 50 | Strict-Transport-Security: 51 | - max-age=31536000; includeSubDomains; preload 52 | body: 53 | encoding: UTF-8 54 | string: '{"error":{"code":"APIKEY.INACTIVE","message":"This api key is no longer 55 | active. Please use a different api key or reactivate this key.","errors":[]}}' 56 | recorded_at: Mon, 29 Jul 2024 18:07:57 GMT 57 | recorded_with: VCR 6.1.0 58 | -------------------------------------------------------------------------------- /spec/cassettes/client/EasyPost_Client_client_object_hooks_notifies_multiple_subscribers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/addresses/adr_123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7666a7da7ee799dc9b0059a23a 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.043603' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb39nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb3nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"error":{"code":"NOT_FOUND","message":"The requested resource could 65 | not be found.","errors":[]}}' 66 | recorded_at: Mon, 29 Jul 2024 18:07:58 GMT 67 | recorded_with: VCR 6.1.0 68 | -------------------------------------------------------------------------------- /spec/cassettes/client/EasyPost_Client_client_object_hooks_removes_subscribers.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/addresses/adr_123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7b66a7da7fe799dc9c0059a2b3 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.059958' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb42nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb3nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"error":{"code":"NOT_FOUND","message":"The requested resource could 65 | not be found.","errors":[]}}' 66 | recorded_at: Mon, 29 Jul 2024 18:07:59 GMT 67 | recorded_with: VCR 6.1.0 68 | -------------------------------------------------------------------------------- /spec/cassettes/client/EasyPost_Client_client_object_hooks_subscribes_to_request_events.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/addresses/adr_123 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d25b576b66a811aee786ac43003430d9 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.049228' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb40nuq 53 | X-Version-Label: 54 | - easypost-202407292115-0457ffd18d-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb4nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"error":{"code":"NOT_FOUND","message":"The requested resource could 65 | not be found.","errors":[]}}' 66 | recorded_at: Mon, 29 Jul 2024 22:03:26 GMT 67 | recorded_with: VCR 6.1.0 68 | -------------------------------------------------------------------------------- /spec/cassettes/client/EasyPost_Client_client_object_hooks_subscribes_to_response_events.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/addresses 6 | body: 7 | encoding: UTF-8 8 | string: '{"name":"Jack Sparrow","street1":"388 Townsend St","street2":"Apt 20","city":"San 9 | Francisco","state":"CA","zip":"94107","country":"US","email":"test@example.com","phone":"5555555555"}' 10 | headers: 11 | Accept-Encoding: 12 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 13 | Accept: 14 | - "*/*" 15 | User-Agent: "" 16 | Host: 17 | - api.easypost.com 18 | Content-Type: 19 | - application/json 20 | Authorization: "" 21 | response: 22 | status: 23 | code: 201 24 | message: Created 25 | headers: 26 | X-Frame-Options: 27 | - SAMEORIGIN 28 | X-Xss-Protection: 29 | - 1; mode=block 30 | X-Content-Type-Options: 31 | - nosniff 32 | X-Download-Options: 33 | - noopen 34 | X-Permitted-Cross-Domain-Policies: 35 | - none 36 | Referrer-Policy: 37 | - strict-origin-when-cross-origin 38 | X-Ep-Request-Uuid: 39 | - 0af7a3d666abeecbe788664b001c17a5 40 | Cache-Control: 41 | - private, no-cache, no-store 42 | Pragma: 43 | - no-cache 44 | Expires: 45 | - '0' 46 | Location: 47 | - "/api/v2/addresses/adr_f072b044504311efae2bac1f6bc539aa" 48 | Content-Type: 49 | - application/json; charset=utf-8 50 | X-Runtime: 51 | - '0.052084' 52 | Transfer-Encoding: 53 | - chunked 54 | X-Node: 55 | - bigweb38nuq 56 | X-Version-Label: 57 | - easypost-202408012013-135f8f9b76-master 58 | X-Backend: 59 | - easypost 60 | X-Proxied: 61 | - extlb2nuq fa152d4755 62 | - intlb4nuq c0f5e722d1 63 | Strict-Transport-Security: 64 | - max-age=31536000; includeSubDomains; preload 65 | body: 66 | encoding: UTF-8 67 | string: '{"id":"adr_f072b044504311efae2bac1f6bc539aa","object":"Address","created_at":"2024-08-01T20:23:39+00:00","updated_at":"2024-08-01T20:23:39+00:00","name":"Jack 68 | Sparrow","company":null,"street1":"388 Townsend St","street2":"Apt 20","city":"San 69 | Francisco","state":"CA","zip":"94107","country":"US","phone":"","email":"","mode":"test","carrier_facility":null,"residential":null,"federal_tax_id":null,"state_tax_id":null,"verifications":{}}' 70 | recorded_at: Thu, 01 Aug 2024 20:23:39 GMT 71 | recorded_with: VCR 6.1.0 72 | -------------------------------------------------------------------------------- /spec/cassettes/customs_info/EasyPost_Services_CustomsInfo_create_creates_a_customs_info.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/customs_infos 6 | body: 7 | encoding: UTF-8 8 | string: '{"customs_info":{"eel_pfc":"NOEEI 30.37(a)","customs_certify":true,"customs_signer":"Steve 9 | Brule","contents_type":"merchandise","contents_explanation":"","restriction_type":"none","non_delivery_option":"return","customs_items":[{"description":"Sweet 10 | shirts","quantity":2,"weight":11,"value":23.25,"hs_tariff_number":"654321","origin_country":"US"}]}}' 11 | headers: 12 | Accept-Encoding: 13 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 14 | Accept: 15 | - "*/*" 16 | User-Agent: "" 17 | Host: 18 | - api.easypost.com 19 | Content-Type: 20 | - application/json 21 | Authorization: "" 22 | response: 23 | status: 24 | code: 201 25 | message: Created 26 | headers: 27 | X-Frame-Options: 28 | - SAMEORIGIN 29 | X-Xss-Protection: 30 | - 1; mode=block 31 | X-Content-Type-Options: 32 | - nosniff 33 | X-Download-Options: 34 | - noopen 35 | X-Permitted-Cross-Domain-Policies: 36 | - none 37 | Referrer-Policy: 38 | - strict-origin-when-cross-origin 39 | X-Ep-Request-Uuid: 40 | - d044fa7966a7da7fe799dc9d0059a319 41 | Cache-Control: 42 | - private, no-cache, no-store 43 | Pragma: 44 | - no-cache 45 | Expires: 46 | - '0' 47 | Location: 48 | - "/api/v2/customs_infos/cstinfo_50c2f22376714fa7b08f6a488ac5e2b1" 49 | Content-Type: 50 | - application/json; charset=utf-8 51 | X-Runtime: 52 | - '0.053727' 53 | Transfer-Encoding: 54 | - chunked 55 | X-Node: 56 | - bigweb53nuq 57 | X-Version-Label: 58 | - easypost-202407291746-57ea285141-master 59 | X-Backend: 60 | - easypost 61 | X-Proxied: 62 | - extlb1nuq fa152d4755 63 | - intlb3nuq c0f5e722d1 64 | Strict-Transport-Security: 65 | - max-age=31536000; includeSubDomains; preload 66 | body: 67 | encoding: UTF-8 68 | string: '{"id":"cstinfo_50c2f22376714fa7b08f6a488ac5e2b1","object":"CustomsInfo","created_at":"2024-07-29T18:07:59Z","updated_at":"2024-07-29T18:07:59Z","contents_explanation":"","contents_type":"merchandise","customs_certify":true,"customs_signer":"Steve 69 | Brule","eel_pfc":"NOEEI 30.37(a)","non_delivery_option":"return","restriction_comments":null,"restriction_type":"none","mode":"test","declaration":null,"customs_items":[{"id":"cstitem_954ba82d861641059c2ca1e9997074cf","object":"CustomsItem","created_at":"2024-07-29T18:07:59Z","updated_at":"2024-07-29T18:07:59Z","description":"Sweet 70 | shirts","hs_tariff_number":"654321","origin_country":"US","quantity":2,"value":"23.25","weight":11.0,"code":null,"mode":"test","manufacturer":null,"currency":null,"eccn":null,"printed_commodity_identifier":null}]}' 71 | recorded_at: Mon, 29 Jul 2024 18:07:59 GMT 72 | recorded_with: VCR 6.1.0 73 | -------------------------------------------------------------------------------- /spec/cassettes/customs_item/EasyPost_Services_CustomsItem_create_creates_a_customs_item.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/customs_items 6 | body: 7 | encoding: UTF-8 8 | string: '{"description":"Sweet shirts","quantity":2,"weight":11,"value":23.25,"hs_tariff_number":"654321","origin_country":"US"}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 201 23 | message: Created 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7666a7da81e799dca00059a481 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Location: 46 | - "/api/v2/customs_items/cstitem_3aa8268b913249469100ec210e0da23f" 47 | Content-Type: 48 | - application/json; charset=utf-8 49 | X-Runtime: 50 | - '0.033868' 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb32nuq 55 | X-Version-Label: 56 | - easypost-202407291746-57ea285141-master 57 | X-Backend: 58 | - easypost 59 | X-Canary: 60 | - direct 61 | X-Proxied: 62 | - extlb1nuq fa152d4755 63 | - intlb3nuq c0f5e722d1 64 | Strict-Transport-Security: 65 | - max-age=31536000; includeSubDomains; preload 66 | body: 67 | encoding: UTF-8 68 | string: '{"id":"cstitem_3aa8268b913249469100ec210e0da23f","object":"CustomsItem","created_at":"2024-07-29T18:08:01Z","updated_at":"2024-07-29T18:08:01Z","description":"Sweet 69 | shirts","hs_tariff_number":"654321","origin_country":"US","quantity":2,"value":"23.25","weight":11.0,"code":null,"mode":"test","manufacturer":null,"currency":null,"eccn":null,"printed_commodity_identifier":null}' 70 | recorded_at: Mon, 29 Jul 2024 18:08:01 GMT 71 | recorded_with: VCR 6.1.0 72 | -------------------------------------------------------------------------------- /spec/cassettes/end_shipper/EasyPost_Services_EndShipper_create_creates_an_EndShipper_object.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/end_shippers 6 | body: 7 | encoding: UTF-8 8 | string: '{"address":{"name":"Jack Sparrow","street1":"388 Townsend St","street2":"Apt 9 | 20","city":"San Francisco","state":"CA","zip":"94107","country":"US","email":"test@example.com","phone":"5555555555"}}' 10 | headers: 11 | Accept-Encoding: 12 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 13 | Accept: 14 | - "*/*" 15 | User-Agent: "" 16 | Host: 17 | - api.easypost.com 18 | Content-Type: 19 | - application/json 20 | Authorization: "" 21 | response: 22 | status: 23 | code: 201 24 | message: Created 25 | headers: 26 | X-Frame-Options: 27 | - SAMEORIGIN 28 | X-Xss-Protection: 29 | - 1; mode=block 30 | X-Content-Type-Options: 31 | - nosniff 32 | X-Download-Options: 33 | - noopen 34 | X-Permitted-Cross-Domain-Policies: 35 | - none 36 | Referrer-Policy: 37 | - strict-origin-when-cross-origin 38 | X-Ep-Request-Uuid: 39 | - d044fa7666a7da82e799dcba0059a5c2 40 | Cache-Control: 41 | - private, no-cache, no-store 42 | Pragma: 43 | - no-cache 44 | Expires: 45 | - '0' 46 | Content-Type: 47 | - application/json; charset=utf-8 48 | X-Runtime: 49 | - '0.069190' 50 | Transfer-Encoding: 51 | - chunked 52 | X-Node: 53 | - bigweb33nuq 54 | X-Version-Label: 55 | - easypost-202407291746-57ea285141-master 56 | X-Backend: 57 | - easypost 58 | X-Proxied: 59 | - extlb1nuq fa152d4755 60 | - intlb3nuq c0f5e722d1 61 | Strict-Transport-Security: 62 | - max-age=31536000; includeSubDomains; preload 63 | body: 64 | encoding: UTF-8 65 | string: '{"id":"es_cdb43810787e402b8a49c0d803b35979","object":"EndShipper","mode":"test","created_at":"2024-07-29T18:08:02+00:00","updated_at":"2024-07-29T18:08:02+00:00","name":"JACK 66 | SPARROW","company":null,"street1":"388 TOWNSEND ST APT 20","street2":"","city":"SAN 67 | FRANCISCO","state":"CA","zip":"94107-1670","country":"US","phone":"","email":""}' 68 | recorded_at: Mon, 29 Jul 2024 18:08:02 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/cassettes/errors/EasyPost_Errors_api_error_assigns_properties_of_an_error_correctly.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/shipments 6 | body: 7 | encoding: UTF-8 8 | string: '{"shipment":{}}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 422 23 | message: Unprocessable Entity 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - 764a527867b8f96ce2ba1cd90039a515 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.018727' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb54nuq 53 | X-Version-Label: 54 | - easypost-202502212131-cc7bde76a5-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq 99aac35317 59 | - intlb3nuq 51d74985a2 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"error":{"code":"PARAMETER.REQUIRED","message":"Missing required parameter.","errors":[{"field":"shipment","message":"cannot 65 | be blank"}]}}' 66 | recorded_at: Fri, 21 Feb 2025 22:08:44 GMT 67 | recorded_with: VCR 6.2.0 68 | -------------------------------------------------------------------------------- /spec/cassettes/errors/EasyPost_Errors_api_error_assigns_properties_of_an_error_correctly_when_returned_via_the_alternative_format.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/claims 6 | body: 7 | encoding: UTF-8 8 | string: '{"tracking_code":"123"}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - 764a527967b8f96ce2ba1cdb0039a535 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.135492' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb40nuq 53 | X-Version-Label: 54 | - easypost-202502212131-cc7bde76a5-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq 99aac35317 59 | - intlb3nuq 51d74985a2 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"error":{"code":"NOT_FOUND","errors":["No eligible insurance found 65 | with provided tracking code."],"message":"The requested resource could not 66 | be found."}}' 67 | recorded_at: Fri, 21 Feb 2025 22:08:44 GMT 68 | recorded_with: VCR 6.2.0 69 | -------------------------------------------------------------------------------- /spec/cassettes/event/EasyPost_Services_Event_all_retrieves_all_events.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/events?page_size=5 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7766a7da88e799dcdf0059ac61 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.191326' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb39nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb3nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"events":[{"description":"tracker.created","id":"evt_7b279f724dd511efbaf177ab1d53f2c3","user_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","status":"pending","created_at":"2024-07-29T18:07:56.134Z","mode":"test","object":"Event"},{"description":"batch.updated","id":"evt_6a6709b64dd511efbe3a5d9fb8d73922","user_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","status":"pending","created_at":"2024-07-29T18:07:28.028Z","mode":"test","object":"Event"},{"description":"batch.created","id":"evt_6a5212184dd511ef86e0199c91ff965e","user_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","status":"pending","created_at":"2024-07-29T18:07:27.891Z","mode":"test","object":"Event"},{"description":"batch.updated","id":"evt_6a1048b04dd511efbfe4377a1ab2cf4a","user_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","status":"pending","created_at":"2024-07-29T18:07:27.459Z","mode":"test","object":"Event"},{"description":"batch.updated","id":"evt_69c43c684dd511efbc3829f3fa1438b2","user_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","status":"pending","created_at":"2024-07-29T18:07:26.961Z","mode":"test","object":"Event"}],"has_more":true}' 65 | recorded_at: Mon, 29 Jul 2024 18:08:09 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/parcel/EasyPost_Services_Parcel_create_creates_a_parcel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/parcels 6 | body: 7 | encoding: UTF-8 8 | string: '{"parcel":{"length":10,"width":8,"height":4,"weight":15.4}}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 201 23 | message: Created 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7666a7dab6e799dd680059de67 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Location: 46 | - "/api/v2/parcels/prcl_887999c98bbc4b1d96b57e4169354181" 47 | Content-Type: 48 | - application/json; charset=utf-8 49 | X-Runtime: 50 | - '0.041851' 51 | Transfer-Encoding: 52 | - chunked 53 | X-Node: 54 | - bigweb34nuq 55 | X-Version-Label: 56 | - easypost-202407291746-57ea285141-master 57 | X-Backend: 58 | - easypost 59 | X-Proxied: 60 | - extlb1nuq fa152d4755 61 | - intlb3nuq c0f5e722d1 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"id":"prcl_887999c98bbc4b1d96b57e4169354181","object":"Parcel","created_at":"2024-07-29T18:08:54Z","updated_at":"2024-07-29T18:08:54Z","length":10.0,"width":8.0,"height":4.0,"predefined_package":null,"weight":15.4,"mode":"test"}' 67 | recorded_at: Mon, 29 Jul 2024 18:08:54 GMT 68 | recorded_with: VCR 6.1.0 69 | -------------------------------------------------------------------------------- /spec/cassettes/referral_customer/EasyPost_Services_ReferralCustomer_add_bank_account_from_stripe_raises_an_error_when_adding_a_bank_account_from_Stripe_fails.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/bank_accounts 6 | body: 7 | encoding: UTF-8 8 | string: '{"financial_connections_id":"fca_0QAc7sDqT4huGUvdf6BahYa9","mandate_data":{"ip_address":"127.0.0.1","user_agent":"Mozilla/5.0","accepted_at":1722510730},"priority":"primary"}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 422 23 | message: Unprocessable Entity 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - 3371ca1967d9db2de2ba06f20029d9a6 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.111726' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb41nuq 53 | X-Version-Label: 54 | - easypost-202503181912-465deca9d7-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq 99aac35317 59 | - intlb4nuq 324e75def6 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"error":{"code":"BANK_ACCOUNT.INVALID_PARAMS","message":"account_holder_name 65 | must be present when creating a Financial Connections payment method","errors":[]}}' 66 | recorded_at: Tue, 18 Mar 2025 20:44:29 GMT 67 | recorded_with: VCR 6.3.1 68 | -------------------------------------------------------------------------------- /spec/cassettes/referral_customer/EasyPost_Services_ReferralCustomer_add_credit_card_from_stripe_raises_an_error_when_adding_a_credit_card_from_Stripe_fails.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/credit_cards 6 | body: 7 | encoding: UTF-8 8 | string: '{"credit_card":{"payment_method_id":"pm_0Pn6bQDqT4huGUvd0CjpRerH","priority":"primary"}}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 404 23 | message: Not Found 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - 3371ca1b67d9db2ce2ba06ef0029d923 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.431126' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb57nuq 53 | X-Version-Label: 54 | - easypost-202503181912-465deca9d7-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq 99aac35317 59 | - intlb4nuq 324e75def6 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"error":{"code":"CREDIT_CARD.NOT_FOUND","message":"Stripe::PaymentMethod 65 | does not exist for the specified reference_id","errors":[]}}' 66 | recorded_at: Tue, 18 Mar 2025 20:44:29 GMT 67 | recorded_with: VCR 6.3.1 68 | -------------------------------------------------------------------------------- /spec/cassettes/referral_customer/EasyPost_Services_ReferralCustomer_create_creates_a_referral_customer.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/referral_customers 6 | body: 7 | encoding: UTF-8 8 | string: '{"user":{"name":"test user","email":"email@example.com","phone":"8888888888"}}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 201 23 | message: Created 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7466a7dad7e799e0fe005a01e1 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.757322' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb32nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Canary: 58 | - direct 59 | X-Proxied: 60 | - extlb1nuq fa152d4755 61 | - intlb4nuq c0f5e722d1 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"id":"user_675bc1a2bcd94af68efe73b4edcfb614","object":"User","parent_id":null,"name":"test 67 | user","phone_number":"","verified":true,"created_at":"2024-07-29T18:09:28Z","default_carbon_offset":false,"has_elevate_access":false,"balance":"0.00000","price_per_shipment":"0.00000","recharge_amount":null,"secondary_recharge_amount":null,"recharge_threshold":null,"has_billing_method":null,"cc_fee_rate":"0.0375","default_insurance_amount":"50.00","insurance_fee_rate":"0.01","insurance_fee_minimum":"0.50","email":"","children":[],"api_keys":[{"object":"ApiKey","key":"","mode":"test","created_at":"2024-07-29T18:09:28Z","active":true,"id":"ak_125b9c58b1c74d979d1784cd12af45e6"},{"object":"ApiKey","key":"","mode":"production","created_at":"2024-07-29T18:09:28Z","active":true,"id":"ak_91e5dd6f05244fe790868c3bf366a6a8"}]}' 68 | recorded_at: Mon, 29 Jul 2024 18:09:28 GMT 69 | recorded_with: VCR 6.1.0 70 | -------------------------------------------------------------------------------- /spec/cassettes/refund/EasyPost_Services_Refund_all_retrieves_all_refunds.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/refunds?page_size=5 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7666a7dae2e799e121005a0c44 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.057496' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb42nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb4nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"refunds":[{"id":"rfnd_c2ca22eea538446f8f2ad624c0a1a49e","object":"Refund","created_at":"2024-07-29T18:09:38Z","updated_at":"2024-07-29T18:09:38Z","tracking_code":"9400100110368068501047","confirmation_number":null,"status":"submitted","carrier":"USPS","shipment_id":"shp_b453ba1d2e504315ae0fd46c98344820"}],"has_more":false}' 65 | recorded_at: Mon, 29 Jul 2024 18:09:38 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/refund/EasyPost_Services_Refund_get_next_page_retrieves_the_next_page_of_a_collection.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/refunds?page_size=5 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7966a7dae3e799e122005a0cb8 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.070245' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb38nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb4nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"refunds":[{"id":"rfnd_c2ca22eea538446f8f2ad624c0a1a49e","object":"Refund","created_at":"2024-07-29T18:09:38Z","updated_at":"2024-07-29T18:09:38Z","tracking_code":"9400100110368068501047","confirmation_number":null,"status":"submitted","carrier":"USPS","shipment_id":"shp_b453ba1d2e504315ae0fd46c98344820"}],"has_more":false}' 65 | recorded_at: Mon, 29 Jul 2024 18:09:38 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/report/EasyPost_Services_Report_all_retrieves_all_reports.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/reports/shipment?page_size=5 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7466a7dae6e799e141005a0fb5 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.099072' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb41nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb4nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"reports":[{"id":"shprep_be0ee6831a3a4029926c69e926e45b1b","object":"ShipmentReport","created_at":"2024-07-29T18:09:41Z","updated_at":"2024-07-29T18:09:41Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"empty","url":null,"url_expires_at":null,"include_children":false},{"id":"shprep_dfdd0a0570d94368b0ea48d24a35ef54","object":"ShipmentReport","created_at":"2024-07-29T18:09:41Z","updated_at":"2024-07-29T18:09:41Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"empty","url":null,"url_expires_at":null,"include_children":false},{"id":"shprep_c27330b6b70442ddb6e9c685d2f3745d","object":"ShipmentReport","created_at":"2024-07-29T18:09:40Z","updated_at":"2024-07-29T18:09:41Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"empty","url":null,"url_expires_at":null,"include_children":false},{"id":"shprep_ca07312e14754c8c89445edec4f18bf6","object":"ShipmentReport","created_at":"2024-07-29T18:09:40Z","updated_at":"2024-07-29T18:09:40Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"empty","url":null,"url_expires_at":null,"include_children":false},{"id":"shprep_e848018e3058446d841ea8fe577c84e6","object":"ShipmentReport","created_at":"2024-07-29T17:48:23Z","updated_at":"2024-07-29T17:48:23Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"empty","url":null,"url_expires_at":null,"include_children":false}],"has_more":true}' 65 | recorded_at: Mon, 29 Jul 2024 18:09:42 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/report/EasyPost_Services_Report_create_creates_a_report.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/reports/shipment 6 | body: 7 | encoding: UTF-8 8 | string: '{"start_date":"2022-06-11","end_date":"2022-06-11"}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 201 23 | message: Created 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7b66a7dae4e799e125005a0dd6 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.076519' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb42nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb3nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"id":"shprep_ca07312e14754c8c89445edec4f18bf6","object":"ShipmentReport","created_at":"2024-07-29T18:09:40Z","updated_at":"2024-07-29T18:09:40Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"new","url":null,"url_expires_at":null,"include_children":false}' 65 | recorded_at: Mon, 29 Jul 2024 18:09:40 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/report/EasyPost_Services_Report_create_creates_a_report_with_custom_additional_columns.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/reports/shipment 6 | body: 7 | encoding: UTF-8 8 | string: '{"start_date":"2022-06-11","end_date":"2022-06-11","additional_columns":["from_name","from_company"]}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 201 23 | message: Created 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7866a7dae5e799e13e005a0e8e 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.053103' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb34nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb3nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"id":"shprep_dfdd0a0570d94368b0ea48d24a35ef54","object":"ShipmentReport","created_at":"2024-07-29T18:09:41Z","updated_at":"2024-07-29T18:09:41Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"new","url":null,"url_expires_at":null,"include_children":false}' 65 | recorded_at: Mon, 29 Jul 2024 18:09:41 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/report/EasyPost_Services_Report_create_creates_a_report_with_custom_columns.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/reports/shipment 6 | body: 7 | encoding: UTF-8 8 | string: '{"start_date":"2022-06-11","end_date":"2022-06-11","columns":["usps_zone"]}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 201 23 | message: Created 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7b66a7dae4e799e13d005a0e37 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.069473' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb41nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb4nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"id":"shprep_c27330b6b70442ddb6e9c685d2f3745d","object":"ShipmentReport","created_at":"2024-07-29T18:09:40Z","updated_at":"2024-07-29T18:09:40Z","start_date":"2022-06-11","end_date":"2022-06-11","mode":"test","status":"new","url":null,"url_expires_at":null,"include_children":false}' 65 | recorded_at: Mon, 29 Jul 2024 18:09:40 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/smart_rate/EasyPost_Services_SmartRate_deliver_by_retrieve_the_estimated_delivery_date.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: post 5 | uri: https://api.easypost.com/v2/smartrate/deliver_by 6 | body: 7 | encoding: UTF-8 8 | string: '{"from_zip":"94107","to_zip":"90277","planned_ship_date":"2024-07-31","carriers":["USPS"]}' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7966a7db29e799e520005a558f 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.054823' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb40nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb4nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"carriers_without_tint_estimates":null,"from_zip":"94107","planned_ship_date":"2024-07-31","results":[{"carrier":"USPS","easypost_time_in_transit_data":{"days_in_transit":{"percentile_50":2,"percentile_75":2,"percentile_85":3,"percentile_90":3,"percentile_95":3,"percentile_97":5,"percentile_99":6},"easypost_estimated_delivery_date":"2024-08-02"},"service":"express"},{"carrier":"USPS","easypost_time_in_transit_data":{"days_in_transit":{"percentile_50":3,"percentile_75":3,"percentile_85":3,"percentile_90":5,"percentile_95":5,"percentile_97":5,"percentile_99":7},"easypost_estimated_delivery_date":"2024-08-03"},"service":"groundadvantage"},{"carrier":"USPS","easypost_time_in_transit_data":{"days_in_transit":{"percentile_50":3,"percentile_75":3,"percentile_85":3,"percentile_90":5,"percentile_95":5,"percentile_97":5,"percentile_99":7},"easypost_estimated_delivery_date":"2024-08-03"},"service":"librarymail"},{"carrier":"USPS","easypost_time_in_transit_data":{"days_in_transit":{"percentile_50":3,"percentile_75":3,"percentile_85":3,"percentile_90":5,"percentile_95":5,"percentile_97":5,"percentile_99":7},"easypost_estimated_delivery_date":"2024-08-03"},"service":"mediamail"},{"carrier":"USPS","easypost_time_in_transit_data":{"days_in_transit":{"percentile_50":3,"percentile_75":3,"percentile_85":3,"percentile_90":5,"percentile_95":5,"percentile_97":5,"percentile_99":7},"easypost_estimated_delivery_date":"2024-08-03"},"service":"priority"}],"saturday_delivery":null,"to_zip":"90277"}' 65 | recorded_at: Mon, 29 Jul 2024 18:10:49 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/cassettes/user/EasyPost_Services_User_all_retrieves_all_child_users.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/users/children?page_size=5 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7966a7db37e799e57e005a6476 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.035757' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb43nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Canary: 58 | - direct 59 | X-Proxied: 60 | - extlb1nuq fa152d4755 61 | - intlb4nuq c0f5e722d1 62 | Strict-Transport-Security: 63 | - max-age=31536000; includeSubDomains; preload 64 | body: 65 | encoding: UTF-8 66 | string: '{"children":[{"id":"user_484dd58db70a4f31b4bb862998cf0e04","object":"User","parent_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","name":"Test 67 | User","phone_number":"","verified":true,"created_at":"2023-05-16T22:01:20Z"},{"id":"user_14e894c0d541459395f4456e7cf4f175","object":"User","parent_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","name":"Test 68 | User","phone_number":"","verified":true,"created_at":"2023-09-27T22:05:26Z"},{"id":"user_f04df3dad13848339a7975d295d6629f","object":"User","parent_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","name":"Test 69 | User","phone_number":"","verified":true,"created_at":"2023-11-30T19:23:22Z"},{"id":"user_44d216445b2f4595a123a18084000d94","object":"User","parent_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","name":"Test 70 | User","phone_number":"","verified":true,"created_at":"2024-07-26T18:36:54Z"},{"id":"user_f3b7021a2d924bedb080d7caa00d753e","object":"User","parent_id":"user_0f6b83e3530b401cb1e8aeaa6a250d4d","name":"Test 71 | User","phone_number":"","verified":true,"created_at":"2024-07-26T18:41:48Z"}],"has_more":true}' 72 | recorded_at: Mon, 29 Jul 2024 18:11:03 GMT 73 | recorded_with: VCR 6.1.0 74 | -------------------------------------------------------------------------------- /spec/cassettes/webhook/EasyPost_Services_Webhook_all_retrieves_all_webhooks.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: https://api.easypost.com/v2/webhooks?page_size=5 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: "" 15 | Host: 16 | - api.easypost.com 17 | Content-Type: 18 | - application/json 19 | Authorization: "" 20 | response: 21 | status: 22 | code: 200 23 | message: OK 24 | headers: 25 | X-Frame-Options: 26 | - SAMEORIGIN 27 | X-Xss-Protection: 28 | - 1; mode=block 29 | X-Content-Type-Options: 30 | - nosniff 31 | X-Download-Options: 32 | - noopen 33 | X-Permitted-Cross-Domain-Policies: 34 | - none 35 | Referrer-Policy: 36 | - strict-origin-when-cross-origin 37 | X-Ep-Request-Uuid: 38 | - d044fa7b66a7db3ce799e586005a692a 39 | Cache-Control: 40 | - private, no-cache, no-store 41 | Pragma: 42 | - no-cache 43 | Expires: 44 | - '0' 45 | Content-Type: 46 | - application/json; charset=utf-8 47 | X-Runtime: 48 | - '0.037354' 49 | Transfer-Encoding: 50 | - chunked 51 | X-Node: 52 | - bigweb35nuq 53 | X-Version-Label: 54 | - easypost-202407291746-57ea285141-master 55 | X-Backend: 56 | - easypost 57 | X-Proxied: 58 | - extlb1nuq fa152d4755 59 | - intlb3nuq c0f5e722d1 60 | Strict-Transport-Security: 61 | - max-age=31536000; includeSubDomains; preload 62 | body: 63 | encoding: UTF-8 64 | string: '{"webhooks":[]}' 65 | recorded_at: Mon, 29 Jul 2024 18:11:08 GMT 66 | recorded_with: VCR 6.1.0 67 | -------------------------------------------------------------------------------- /spec/claim_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::Claim do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates a claim object' do 10 | amount = '100' 11 | insured_shipment = create_and_insure_shipment(client, amount) 12 | claim_data = Fixture.basic_claim 13 | claim_data[:tracking_code] = insured_shipment.tracking_code 14 | claim_data[:amount] = amount 15 | claim = client.claim.create(claim_data) 16 | 17 | expect(claim).to be_an_instance_of(EasyPost::Models::Claim) 18 | expect(claim.id).to match('clm_') 19 | end 20 | end 21 | 22 | describe '.retrieve' do 23 | it 'retrieves a claim object' do 24 | amount = '100' 25 | insured_shipment = create_and_insure_shipment(client, amount) 26 | claim_data = Fixture.basic_claim 27 | claim_data[:tracking_code] = insured_shipment.tracking_code 28 | claim_data[:amount] = amount 29 | claim = client.claim.create(claim_data) 30 | 31 | retrieved_claim = client.claim.retrieve(claim.id) 32 | 33 | expect(retrieved_claim).to be_an_instance_of(EasyPost::Models::Claim) 34 | expect(retrieved_claim.id).to eq(claim.id) 35 | end 36 | end 37 | 38 | describe '.all' do 39 | it 'retrieves all claim objects' do 40 | all_claims = client.claim.all( 41 | page_size: Fixture.page_size, 42 | ) 43 | expect(all_claims[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to be_a(Hash) 44 | 45 | claim_array = all_claims.claims 46 | 47 | expect(claim_array.count).to be <= Fixture.page_size 48 | expect(all_claims.has_more).not_to be_nil 49 | expect(claim_array).to all(be_an_instance_of(EasyPost::Models::Claim)) 50 | end 51 | end 52 | 53 | describe '.get_next_page' do 54 | it 'retrieves the next page of a collection' do 55 | first_page = client.claim.all( 56 | page_size: Fixture.page_size, 57 | ) 58 | 59 | begin 60 | next_page = client.claim.get_next_page(first_page) 61 | 62 | first_page_first_id = first_page.claims.first.id 63 | next_page_first_id = next_page.claims.first.id 64 | 65 | expect(first_page_first_id).not_to eq(next_page_first_id) 66 | expect(first_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to eq( 67 | next_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY], 68 | ) 69 | rescue EasyPost::Errors::EndOfPaginationError => e 70 | # If we get an error, make sure it's because there are no more pages. 71 | expect(e.message).to eq(EasyPost::Constants::NO_MORE_PAGES) 72 | end 73 | end 74 | end 75 | 76 | describe '.cancel' do 77 | it 'cancels an claim' do 78 | amount = '100' 79 | insured_shipment = create_and_insure_shipment(client, amount) 80 | claim_data = Fixture.basic_claim 81 | claim_data[:tracking_code] = insured_shipment.tracking_code 82 | claim_data[:amount] = amount 83 | claim = client.claim.create(claim_data) 84 | 85 | cancelled_claim = client.claim.cancel(claim.id) 86 | 87 | expect(cancelled_claim).to be_an_instance_of(EasyPost::Models::Claim) 88 | expect(cancelled_claim.id).to match('clm_') 89 | expect(cancelled_claim.status).to eq('cancelled') 90 | end 91 | end 92 | end 93 | -------------------------------------------------------------------------------- /spec/customs_info_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::CustomsInfo do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates a customs info' do 10 | customs_info = client.customs_info.create(Fixture.basic_customs_info) 11 | 12 | expect(customs_info).to be_an_instance_of(EasyPost::Models::CustomsInfo) 13 | expect(customs_info.customs_items).to all(be_an_instance_of(EasyPost::Models::CustomsItem)) 14 | expect(customs_info.id).to match('cstinfo_') 15 | expect(customs_info.eel_pfc).to eq('NOEEI 30.37(a)') 16 | end 17 | end 18 | 19 | describe '.retrieve' do 20 | it 'retrieves a customs info' do 21 | customs_info = client.customs_info.create(Fixture.basic_customs_info) 22 | retrieved_customs_info = client.customs_info.retrieve(customs_info.id) 23 | 24 | expect(retrieved_customs_info).to be_an_instance_of(EasyPost::Models::CustomsInfo) 25 | expect(retrieved_customs_info.to_s).to eq(customs_info.to_s) 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/customs_item_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::CustomsItem do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates a customs item' do 10 | customs_item = client.customs_item.create(Fixture.basic_customs_item) 11 | 12 | expect(customs_item).to be_an_instance_of(EasyPost::Models::CustomsItem) 13 | expect(customs_item.id).to match('cstitem_') 14 | expect(customs_item.value).to eq('23.25') 15 | end 16 | end 17 | 18 | describe '.retrieve' do 19 | it 'retrieves a customs item' do 20 | customs_item = client.customs_item.create(Fixture.basic_customs_item) 21 | retrieved_customs_item = client.customs_item.retrieve(customs_item.id) 22 | 23 | expect(retrieved_customs_item).to be_an_instance_of(EasyPost::Models::CustomsItem) 24 | expect(retrieved_customs_item.to_s).to eq(customs_item.to_s) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/end_shipper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::EndShipper do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates an EndShipper object' do 10 | end_shipper_address = client.end_shipper.create(Fixture.ca_address1) 11 | 12 | expect(end_shipper_address).to be_an_instance_of(EasyPost::Models::EndShipper) 13 | expect(end_shipper_address.id).to match('es_') 14 | expect(end_shipper_address.street1).to eq('388 TOWNSEND ST APT 20') 15 | end 16 | end 17 | 18 | describe '.retrieve' do 19 | it 'retrieves an EndShipper object' do 20 | end_shipper_address = client.end_shipper.create(Fixture.ca_address1) 21 | retrieved_end_shipper_address = client.end_shipper.retrieve(end_shipper_address.id) 22 | 23 | expect(retrieved_end_shipper_address).to be_an_instance_of(EasyPost::Models::EndShipper) 24 | expect(retrieved_end_shipper_address.street1).to eq(end_shipper_address.street1) 25 | end 26 | end 27 | 28 | describe '.all' do 29 | it 'retrieves all EndShipper objects' do 30 | end_shippers = client.end_shipper.all(page_size: Fixture.page_size) 31 | 32 | end_shippers_array = end_shippers.end_shippers 33 | 34 | expect(end_shippers_array.count).to be <= Fixture.page_size 35 | expect(end_shippers.has_more).not_to be_nil 36 | expect(end_shippers_array).to all(be_an_instance_of(EasyPost::Models::EndShipper)) 37 | end 38 | end 39 | 40 | describe '.update' do 41 | it 'updates the EndShipper object' do 42 | end_shipper = client.end_shipper.create(Fixture.ca_address1) 43 | 44 | params = Fixture.ca_address1 45 | params['name'] = 'Captain Sparrow' 46 | 47 | updated_end_shipper = client.end_shipper.update(end_shipper.id, params) 48 | 49 | expect(updated_end_shipper).to be_an_instance_of(EasyPost::Models::EndShipper) 50 | expect(updated_end_shipper.id).to match('es_') 51 | expect(updated_end_shipper.name).to eq('CAPTAIN SPARROW') # Address verification will capitalize the name 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/errors_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Errors do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe 'api error' do 9 | it 'assigns properties of an error correctly' do 10 | client.shipment.create({}) 11 | rescue EasyPost::Errors::ApiError => e 12 | expect(e.status_code).to eq(422) 13 | expect(e.code).to eq('PARAMETER.REQUIRED') 14 | expect(e.message).to eq('Missing required parameter.') 15 | expect(e.errors.first).to eq({ 'field' => 'shipment', 'message' => 'cannot be blank' }) 16 | end 17 | 18 | it 'assigns properties of an error correctly when returned via the alternative format' do 19 | claim_data = { tracking_code: '123' } # Intentionally pass a bad tracking code 20 | 21 | begin 22 | client.claim.create(claim_data) 23 | rescue EasyPost::Errors::ApiError => e 24 | expect(e.status_code).to eq(404) 25 | expect(e.code).to eq('NOT_FOUND') 26 | expect(e.message).to eq('The requested resource could not be found.') 27 | expect(e.errors.first).to eq('No eligible insurance found with provided tracking code.') 28 | end 29 | end 30 | 31 | it 'concatenates error messages that are a list' do 32 | error = EasyPost::Errors::ApiError.new(message: %w[Error1 Error2]) 33 | 34 | expect(error.message).to eq('Error1, Error2') 35 | end 36 | 37 | it 'concatenates error messages that are a dict' do 38 | message_data = { 'errors' => ['Bad format 1', 'Bad format 2'] } 39 | error = EasyPost::Errors::ApiError.new(message: message_data) 40 | 41 | expect(error.message).to eq('Bad format 1, Bad format 2') 42 | end 43 | 44 | it 'concatenates error messages that have an invalid format' do 45 | message_data = { 46 | 'errors' => ['Bad format 1', 'Bad format 2'], 47 | 'bad_data' => [ 48 | { 49 | 'first_message' => 'Bad format 3', 50 | 'second_message' => 'Bad format 4', 51 | 'third_message' => 'Bad format 5', 52 | }, 53 | ], 54 | } 55 | error = EasyPost::Errors::ApiError.new(message: message_data) 56 | 57 | expect(error.message).to eq('Bad format 1, Bad format 2, Bad format 3, Bad format 4, Bad format 5') 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /spec/insurance_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::Insurance do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates an insurance object' do 10 | shipment = client.shipment.create(Fixture.one_call_buy_shipment) 11 | 12 | insurance_data = Fixture.basic_insurance 13 | insurance_data[:tracking_code] = shipment.tracking_code 14 | 15 | insurance = client.insurance.create(insurance_data) 16 | 17 | expect(insurance).to be_an_instance_of(EasyPost::Models::Insurance) 18 | expect(insurance.id).to match('ins_') 19 | end 20 | end 21 | 22 | describe '.retrieve' do 23 | it 'retrieves an insurance object' do 24 | shipment = client.shipment.create(Fixture.one_call_buy_shipment) 25 | 26 | insurance_data = Fixture.basic_insurance 27 | insurance_data[:tracking_code] = shipment.tracking_code 28 | 29 | insurance = client.insurance.create(insurance_data) 30 | 31 | retrieved_insurance = client.insurance.retrieve(insurance.id) 32 | 33 | expect(retrieved_insurance).to be_an_instance_of(EasyPost::Models::Insurance) 34 | expect(retrieved_insurance.id).to eq(insurance.id) 35 | end 36 | end 37 | 38 | describe '.all' do 39 | it 'retrieves all insurance objects' do 40 | all_insurances = client.insurance.all( 41 | page_size: Fixture.page_size, 42 | ) 43 | expect(all_insurances[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to be_a(Hash) 44 | 45 | insurance_array = all_insurances.insurances 46 | 47 | expect(insurance_array.count).to be <= Fixture.page_size 48 | expect(all_insurances.has_more).not_to be_nil 49 | expect(insurance_array).to all(be_an_instance_of(EasyPost::Models::Insurance)) 50 | end 51 | end 52 | 53 | describe '.get_next_page' do 54 | it 'retrieves the next page of a collection' do 55 | first_page = client.insurance.all( 56 | page_size: Fixture.page_size, 57 | ) 58 | 59 | begin 60 | next_page = client.insurance.get_next_page(first_page) 61 | 62 | first_page_first_id = first_page.insurances.first.id 63 | next_page_first_id = next_page.insurances.first.id 64 | 65 | expect(first_page_first_id).not_to eq(next_page_first_id) 66 | expect(first_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to eq( 67 | next_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY], 68 | ) 69 | rescue EasyPost::Errors::EndOfPaginationError => e 70 | # If we get an error, make sure it's because there are no more pages. 71 | expect(e.message).to eq(EasyPost::Constants::NO_MORE_PAGES) 72 | end 73 | end 74 | end 75 | 76 | describe '.refund' do 77 | it 'cancels an insurance' do 78 | insurance_data = Fixture.basic_insurance 79 | insurance_data[:tracking_code] = 'EZ1000000001' 80 | 81 | insurance = client.insurance.create(insurance_data) 82 | cancelled_insurance = client.insurance.refund(insurance.id) 83 | 84 | expect(cancelled_insurance).to be_an_instance_of(EasyPost::Models::Insurance) 85 | expect(cancelled_insurance.id).to match('ins_') 86 | expect(cancelled_insurance.status).to eq('cancelled') 87 | expect(cancelled_insurance.messages[0]).to eq('Insurance was cancelled by the user.') 88 | end 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /spec/order_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::Order do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates an order' do 10 | order = client.order.create(Fixture.basic_order) 11 | 12 | expect(order).to be_an_instance_of(EasyPost::Models::Order) 13 | expect(order.id).to match('order_') 14 | expect(order.rates).not_to be_nil 15 | end 16 | end 17 | 18 | describe '.retrieve' do 19 | it 'retrieves an order' do 20 | order = client.order.create(Fixture.basic_order) 21 | retrieved_order = client.order.retrieve(order.id) 22 | 23 | expect(retrieved_order).to be_an_instance_of(EasyPost::Models::Order) 24 | expect(retrieved_order.id).to eq(order.id) 25 | end 26 | end 27 | 28 | describe '.get_rates' do 29 | it 'retrieves rates for an order' do 30 | order = client.order.create(Fixture.basic_order) 31 | rates = client.order.get_rates(order.id) 32 | 33 | rates_array = rates.rates 34 | 35 | expect(rates_array).to be_an_instance_of(Array) 36 | expect(rates_array).to all(be_an_instance_of(EasyPost::Models::Rate)) 37 | end 38 | end 39 | 40 | describe '.buy' do 41 | it 'buys an order' do 42 | order = client.order.create(Fixture.basic_order) 43 | 44 | bought_order = client.order.buy( 45 | order.id, 46 | { 47 | carrier: Fixture.usps, 48 | service: Fixture.usps_service, 49 | }, 50 | ) 51 | 52 | shipments_array = bought_order.shipments 53 | 54 | shipments_array.each do |shipment| 55 | expect(shipment.postage_label).not_to be_nil 56 | end 57 | end 58 | 59 | it 'buys an order with a rate object' do 60 | order = client.order.create(Fixture.basic_order) 61 | 62 | bought_order = client.order.buy(order.id, order.lowest_rate) 63 | 64 | shipments_array = bought_order.shipments 65 | 66 | shipments_array.each do |shipment| 67 | expect(shipment.postage_label).not_to be_nil 68 | end 69 | end 70 | end 71 | 72 | describe '.lowest_rate' do 73 | it 'tests various usage alterations of the lowest_rate method' do 74 | shipment = client.order.create(Fixture.basic_order) 75 | 76 | # Test lowest rate with no filters 77 | lowest_rate = shipment.lowest_rate 78 | expect(lowest_rate.service).to eq('GroundAdvantage') 79 | expect(lowest_rate.rate).to eq('11.40') 80 | expect(lowest_rate.carrier).to eq('USPS') 81 | 82 | # Test lowest rate with service filter (this rate is higher than the lowest but should filter) 83 | lowest_rate = shipment.lowest_rate([], ['Priority']) 84 | expect(lowest_rate.service).to eq('Priority') 85 | expect(lowest_rate.rate).to eq('14.48') 86 | expect(lowest_rate.carrier).to eq('USPS') 87 | 88 | # Test lowest rate with carrier filter (should error due to bad carrier) 89 | expect { 90 | shipment.lowest_rate(['BAD CARRIER'], []) 91 | }.to raise_error(EasyPost::Errors::FilteringError, EasyPost::Constants::NO_MATCHING_RATES) 92 | end 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /spec/parcel_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::Parcel do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates a parcel' do 10 | parcel = client.parcel.create(Fixture.basic_parcel) 11 | 12 | expect(parcel).to be_an_instance_of(EasyPost::Models::Parcel) 13 | expect(parcel.id).to match('prcl_') 14 | expect(parcel.weight).to eq(15.4) 15 | end 16 | end 17 | 18 | describe '.retrieve' do 19 | it 'retrieves a parcel' do 20 | parcel = client.parcel.create(Fixture.basic_parcel) 21 | retrieved_parcel = client.parcel.retrieve(parcel.id) 22 | 23 | expect(retrieved_parcel).to be_an_instance_of(EasyPost::Models::Parcel) 24 | expect(retrieved_parcel.to_s).to eq(parcel.to_s) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/rate_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::Rate do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.retrieve' do 9 | it 'retrieves a rate' do 10 | shipment = client.shipment.create(Fixture.basic_shipment) 11 | 12 | rate = client.rate.retrieve(shipment.rates[0].id) 13 | 14 | expect(rate).to be_an_instance_of(EasyPost::Models::Rate) 15 | expect(rate.id).to match('rate') 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/refund_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::Refund do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates a refund' do 10 | shipment = client.shipment.create(Fixture.one_call_buy_shipment) 11 | retrieved_shipment = client.shipment.retrieve(shipment.id) # We need to retrieve the shipment so that the tracking_code has time to populate 12 | 13 | refund = client.refund.create( 14 | carrier: Fixture.usps, 15 | tracking_codes: [retrieved_shipment.tracking_code], 16 | ) 17 | 18 | expect(refund[0].id).to match('rfnd') 19 | expect(refund[0].status).to eq('submitted') 20 | end 21 | end 22 | 23 | describe '.all' do 24 | it 'retrieves all refunds' do 25 | refunds = client.refund.all( 26 | page_size: Fixture.page_size, 27 | ) 28 | expect(refunds[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to be_a(Hash) 29 | 30 | refunds_array = refunds.refunds 31 | 32 | expect(refunds_array.count).to be <= Fixture.page_size 33 | expect(refunds.has_more).not_to be_nil 34 | expect(refunds_array).to all(be_an_instance_of(EasyPost::Models::Refund)) 35 | end 36 | end 37 | 38 | describe '.get_next_page' do 39 | it 'retrieves the next page of a collection' do 40 | first_page = client.refund.all( 41 | page_size: Fixture.page_size, 42 | ) 43 | 44 | begin 45 | next_page = client.refund.get_next_page(first_page) 46 | 47 | first_page_first_id = first_page.refunds.first.id 48 | next_page_first_id = next_page.refunds.first.id 49 | 50 | # Did we actually get a new page? 51 | expect(first_page_first_id).not_to eq(next_page_first_id) 52 | expect(first_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to eq( 53 | next_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY], 54 | ) 55 | rescue EasyPost::Errors::EndOfPaginationError => e 56 | # If we get an error, make sure it's because there are no more pages. 57 | expect(e.message).to eq(EasyPost::Constants::NO_MORE_PAGES) 58 | end 59 | end 60 | end 61 | 62 | describe '.retrieve' do 63 | it 'retrieves a refund' do 64 | refunds = client.refund.all( 65 | page_size: Fixture.page_size, 66 | ) 67 | 68 | refunds_array = refunds.refunds 69 | 70 | retrieved_refund = client.refund.retrieve(refunds_array[0].id) 71 | 72 | expect(retrieved_refund).to be_an_instance_of(EasyPost::Models::Refund) 73 | expect(retrieved_refund.id).to eq(refunds_array[0].id) 74 | end 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /spec/scan_form_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::ScanForm do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates a scanform' do 10 | shipment = client.shipment.create(Fixture.one_call_buy_shipment) 11 | 12 | scan_form = client.scan_form.create( 13 | shipments: [shipment], 14 | ) 15 | 16 | expect(scan_form).to be_an_instance_of(EasyPost::Models::ScanForm) 17 | expect(scan_form.id).to match('sf_') 18 | end 19 | end 20 | 21 | describe '.retrieve' do 22 | it 'retrieves a scanform' do 23 | shipment = client.shipment.create(Fixture.one_call_buy_shipment) 24 | 25 | scan_form = client.scan_form.create( 26 | shipments: [shipment], 27 | ) 28 | 29 | retrieved_scan_form = client.scan_form.retrieve(scan_form.id) 30 | 31 | expect(retrieved_scan_form).to be_an_instance_of(EasyPost::Models::ScanForm) 32 | expect(retrieved_scan_form.to_s).to eq(scan_form.to_s) 33 | end 34 | end 35 | 36 | describe '.all' do 37 | it 'retrieves all scanforms' do 38 | scan_forms = client.scan_form.all( 39 | page_size: Fixture.page_size, 40 | ) 41 | expect(scan_forms[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to be_a(Hash) 42 | 43 | scan_forms_array = scan_forms.scan_forms 44 | 45 | expect(scan_forms_array.count).to be <= Fixture.page_size 46 | expect(scan_forms.has_more).not_to be_nil 47 | expect(scan_forms_array).to all(be_an_instance_of(EasyPost::Models::ScanForm)) 48 | end 49 | end 50 | 51 | describe '.get_next_page' do 52 | it 'retrieves the next page of a collection' do 53 | first_page = client.scan_form.all( 54 | page_size: Fixture.page_size, 55 | ) 56 | 57 | begin 58 | next_page = client.scan_form.get_next_page(first_page) 59 | 60 | first_page_first_id = first_page.scan_forms.first.id 61 | next_page_first_id = next_page.scan_forms.first.id 62 | 63 | # Did we actually get a new page? 64 | expect(first_page_first_id).not_to eq(next_page_first_id) 65 | expect(first_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to eq( 66 | next_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY], 67 | ) 68 | rescue EasyPost::Errors::EndOfPaginationError => e 69 | # If we get an error, make sure it's because there are no more pages. 70 | expect(e.message).to eq(EasyPost::Constants::NO_MORE_PAGES) 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /spec/smart_rate_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::SmartRate do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.deliver_by' do 9 | it 'retrieve the estimated delivery date' do 10 | params = { 11 | from_zip: Fixture.ca_address1['zip'], 12 | to_zip: Fixture.ca_address2['zip'], 13 | planned_ship_date: Fixture.planned_ship_date, 14 | carriers: [Fixture.usps], 15 | } 16 | 17 | rates = client.smart_rate.estimate_delivery_date(params) 18 | 19 | expect(rates['results'].all? { |rate| rate['easypost_time_in_transit_data'] }).not_to be_nil 20 | end 21 | end 22 | 23 | describe '.deliver_on' do 24 | it 'retrieve a recommended ship date' do 25 | params = { 26 | from_zip: Fixture.ca_address1['zip'], 27 | to_zip: Fixture.ca_address2['zip'], 28 | desired_delivery_date: Fixture.desired_delivery_date, 29 | carriers: [Fixture.usps], 30 | } 31 | 32 | rates = client.smart_rate.recommend_ship_date(params) 33 | 34 | expect(rates['results'].all? { |rate| rate['easypost_time_in_transit_data'] }).not_to be_nil 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Simplecov must be loaded before everything else to work properly 4 | require 'simplecov' 5 | require 'simplecov-lcov' 6 | 7 | SimpleCov::Formatter::LcovFormatter.config do |config| 8 | config.report_with_single_file = true 9 | end 10 | 11 | SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter.new( 12 | [ 13 | SimpleCov::Formatter::HTMLFormatter, 14 | SimpleCov::Formatter::LcovFormatter, 15 | ], 16 | ) 17 | 18 | SimpleCov.start do 19 | track_files 'lib/**/*.rb' 20 | add_filter '/spec/' 21 | add_filter 'lib/easypost/version.rb' 22 | enable_coverage :branch 23 | minimum_coverage 90 24 | end 25 | 26 | require 'open-uri' 27 | require 'easypost' 28 | 29 | Dir['./spec/support/**/*.rb'].sort.each { |f| require f } 30 | 31 | module ShipmentHelper 32 | def create_and_insure_shipment(client, amount) 33 | # This method will be mainly by claims testing. 34 | shipment = client.shipment.create(Fixture.basic_shipment) 35 | rate = shipment.lowest_rate 36 | client.shipment.buy(shipment.id, rate: rate, insurance: amount) 37 | end 38 | end 39 | 40 | RSpec.configure do |config| 41 | config.around do |example| 42 | # Automatically wrap the test in VCR to avoid forgetting it. 43 | path = example.file_path.gsub('_spec.rb', '').gsub('./spec/', '') 44 | description = example.full_description 45 | cassette_path = "#{path}/#{description}" 46 | VCR.use_cassette( 47 | cassette_path, 48 | allow_unused_http_interactions: true, 49 | ) do 50 | example.call 51 | end 52 | 53 | check_expired_cassette(cassette_path) 54 | end 55 | 56 | # Include the helper module 57 | config.include ShipmentHelper 58 | end 59 | 60 | # Checks for an expired cassette and warns if it is too old and must be re-recorded 61 | def check_expired_cassette(cassette_path) 62 | full_cassette_path = "#{"spec/cassettes/#{cassette_path}".gsub('::', '_').gsub(' ', '_').gsub('.', '_')}.yml" 63 | seconds_in_day = 86_400 64 | expiration_days = 365 65 | expiration_seconds = seconds_in_day * expiration_days 66 | 67 | return unless File.exist?(full_cassette_path) 68 | 69 | cassette_timestamp = File.mtime(full_cassette_path) 70 | expiration_timestamp = DateTime.parse((cassette_timestamp + expiration_seconds).to_s) 71 | current_timestamp = DateTime.now 72 | 73 | return unless current_timestamp > expiration_timestamp 74 | 75 | warn "#{full_cassette_path} is older than #{expiration_days} days and has expired. Please re-record the cassette" 76 | end 77 | -------------------------------------------------------------------------------- /spec/support/vcr.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'vcr' 4 | require 'json' 5 | 6 | REPLACEMENT_STRING = '' 7 | REPLACEMENT_ARRAY = [].freeze 8 | REPLACEMENT_HASH = {}.freeze 9 | 10 | SCRUBBERS = [ 11 | ['client_ip', REPLACEMENT_STRING], 12 | ['credentials', REPLACEMENT_HASH], 13 | ['email', REPLACEMENT_STRING], 14 | ['fields', REPLACEMENT_ARRAY], 15 | ['key', REPLACEMENT_STRING], 16 | ['phone', REPLACEMENT_STRING], 17 | ['phone_number', REPLACEMENT_STRING], 18 | ['test_credentials', REPLACEMENT_HASH], 19 | ].freeze 20 | 21 | VCR.configure do |config| 22 | config.cassette_library_dir = 'spec/cassettes' 23 | config.hook_into :webmock 24 | 25 | config.default_cassette_options = { 26 | match_requests_on: [ 27 | :body, 28 | :method, 29 | :uri, 30 | ], 31 | allow_unused_http_interactions: false, 32 | } 33 | 34 | config.filter_sensitive_data(REPLACEMENT_STRING) { Fixture.credit_card_details['number'] } 35 | config.filter_sensitive_data(REPLACEMENT_STRING) { Fixture.credit_card_details['cvc'] } 36 | 37 | config.before_record do |interaction| 38 | scrub_request_headers(interaction) 39 | scrub_response_bodies(interaction) 40 | end 41 | 42 | def scrub_request_headers(interaction) 43 | # rubocop:disable Style/GuardClause 44 | unless interaction.request.headers['Authorization'].nil? 45 | interaction.request.headers['Authorization'] = REPLACEMENT_STRING 46 | end 47 | unless interaction.request.headers['User-Agent'].nil? 48 | interaction.request.headers['User-Agent'] = REPLACEMENT_STRING 49 | end 50 | # rubocop:enable Style/GuardClause 51 | end 52 | 53 | # Scrub sensitive data from response bodies (at the root level or in a root list) prior to recording the cassette 54 | # This DOES NOT scrub data in nested objects or lists 55 | def scrub_response_bodies(interaction) 56 | SCRUBBERS.each do |scrubber| 57 | next unless interaction.response.body && !interaction.response.body.empty? 58 | 59 | response_body = JSON.parse(interaction.response.body) 60 | 61 | scrub_data(response_body, scrubber) 62 | 63 | interaction.response.body = response_body.to_json 64 | end 65 | end 66 | 67 | private 68 | 69 | def scrub_data(data, scrubber) 70 | key = scrubber[0] 71 | replacement = scrubber[1] 72 | 73 | # Root-level list scrubbing 74 | case data 75 | when Array 76 | data.each do |item| 77 | if item.key?(key) 78 | item[key] = replacement 79 | end 80 | end 81 | when Hash 82 | # Root-level key scrubbing 83 | if data.key?(key) 84 | data[key] = replacement 85 | else 86 | # Nested scrubbing 87 | data.each_value do |top_values| 88 | case top_values 89 | when Array 90 | top_values.each_entry do |nested_item| 91 | scrub_data(nested_item, scrubber) 92 | end 93 | when Hash 94 | scrub_data(top_values, scrubber) 95 | end 96 | end 97 | end 98 | end 99 | 100 | data 101 | end 102 | end 103 | -------------------------------------------------------------------------------- /spec/tracker_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe EasyPost::Services::Tracker do 6 | let(:client) { EasyPost::Client.new(api_key: ENV['EASYPOST_TEST_API_KEY']) } 7 | 8 | describe '.create' do 9 | it 'creates a tracker' do 10 | tracker = client.tracker.create( 11 | tracking_code: 'EZ1000000001', 12 | ) 13 | 14 | expect(tracker).to be_an_instance_of(EasyPost::Models::Tracker) 15 | expect(tracker.id).to match('trk_') 16 | expect(tracker.status).to eq('pre_transit') 17 | end 18 | end 19 | 20 | describe '.retrieve' do 21 | it 'retrieves a tracker' do 22 | tracker = client.tracker.create( 23 | tracking_code: 'EZ1000000001', 24 | ) 25 | 26 | retrieved_tracker = client.tracker.retrieve(tracker.id) 27 | 28 | expect(retrieved_tracker).to be_an_instance_of(EasyPost::Models::Tracker) 29 | expect(retrieved_tracker.id).to eq(tracker.id) 30 | end 31 | end 32 | 33 | describe '.all' do 34 | it 'retrieves all trackers' do 35 | trackers = client.tracker.all( 36 | page_size: Fixture.page_size, 37 | ) 38 | expect(trackers[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to be_a(Hash) 39 | expect(trackers[:_filters]).to include(:tracking_code, :carrier) 40 | 41 | trackers_array = trackers.trackers 42 | 43 | expect(trackers_array.count).to be <= Fixture.page_size 44 | expect(trackers.has_more).not_to be_nil 45 | expect(trackers_array).to all(be_an_instance_of(EasyPost::Models::Tracker)) 46 | end 47 | 48 | it 'stores the params used to retrieve the trackers' do 49 | tracking_code = 'EZ1000000001' 50 | carrier = 'USPS' 51 | 52 | trackers = client.tracker.all( 53 | page_size: Fixture.page_size, 54 | tracking_code: tracking_code, 55 | carrier: carrier, 56 | ) 57 | 58 | expect(trackers[:_filters][:tracking_code]).to eq(tracking_code) 59 | expect(trackers[:_filters][:carrier]).to eq(carrier) 60 | end 61 | end 62 | 63 | describe '.get_next_page' do 64 | it 'retrieves the next page of a collection' do 65 | first_page = client.tracker.all( 66 | page_size: Fixture.page_size, 67 | ) 68 | 69 | begin 70 | next_page = client.tracker.get_next_page(first_page) 71 | 72 | first_page_first_id = first_page.trackers.first.id 73 | next_page_first_id = next_page.trackers.first.id 74 | 75 | # Did we actually get a new page? 76 | expect(first_page_first_id).not_to eq(next_page_first_id) 77 | expect(first_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY]).to eq( 78 | next_page[EasyPost::InternalUtilities::Constants::FILTERS_KEY], 79 | ) 80 | rescue EasyPost::Errors::EndOfPaginationError => e 81 | # If we get an error, make sure it's because there are no more pages. 82 | expect(e.message).to eq(EasyPost::Constants::NO_MORE_PAGES) 83 | end 84 | end 85 | end 86 | end 87 | --------------------------------------------------------------------------------