Class: Edi::Amazon::ShipWithAmazon
- Inherits:
-
Object
- Object
- Edi::Amazon::ShipWithAmazon
- Defined in:
- app/services/edi/amazon/ship_with_amazon.rb
Overview
Client for Amazon's Buy Shipping API (Shipping V2)
Enables purchasing shipping labels with Amazon Buy Shipping protections
See: https://developer-docs.shipping.amazon.com/apis/docs/shipping-api-v2-reference
Flow:
- get_rates - Get eligible shipping service offerings for a shipment
- purchase_shipment - Purchase a label using a rate from get_rates
- get_shipment_documents - Download the label PDF
- cancel_shipment - Void/cancel a purchased shipment
- get_tracking - Get tracking info for a purchased shipment
Defined Under Namespace
Classes: CancelResult, DocumentResult, PurchaseResult, RatesResult, TrackingResult
Constant Summary collapse
- BASE_PATH =
'/shipping/v2'- BUSINESS_ID_MAP =
Amazon Shipping business IDs per marketplace region
{ amazon_seller_central_us: 'AmazonShipping_US', amazon_seller_central_ca: 'AmazonShipping_US', # CA uses NA endpoint with US business ID amazon_seller_central_uk: 'AmazonShipping_UK', amazon_seller_central_fr: 'AmazonShipping_FR', amazon_seller_central_de: 'AmazonShipping_FR', # DE routes through FR business amazon_seller_central_es: 'AmazonShipping_ES', amazon_seller_central_it: 'AmazonShipping_IT' }.freeze
Instance Attribute Summary collapse
-
#api_log ⇒ Object
readonly
Returns the value of attribute api_log.
-
#logger ⇒ Object
readonly
Returns the value of attribute logger.
-
#orchestrator ⇒ Object
readonly
Returns the value of attribute orchestrator.
-
#transport ⇒ Object
readonly
Returns the value of attribute transport.
Instance Method Summary collapse
-
#cancel_shipment(shipment_id) ⇒ CancelResult
PUT /shipping/v2/shipments/shipmentId/cancel Cancels a purchased shipment.
-
#get_rates(options) ⇒ RatesResult
POST /shipping/v2/shipments/rates Returns eligible shipping service offerings for a shipment.
-
#get_shipment_documents(shipment_id, package_client_reference_id, format: 'PDF') ⇒ DocumentResult
GET /shipping/v2/shipments/shipmentId/documents Returns shipping documents (label PDF) for a package.
-
#get_tracking(tracking_id, carrier_id) ⇒ TrackingResult
GET /shipping/v2/tracking Returns tracking information for a purchased shipment.
-
#initialize(orchestrator) ⇒ ShipWithAmazon
constructor
A new instance of ShipWithAmazon.
-
#purchase_shipment(options) ⇒ PurchaseResult
POST /shipping/v2/shipments Purchases a shipping service and returns label documents.
Constructor Details
#initialize(orchestrator) ⇒ ShipWithAmazon
Returns a new instance of ShipWithAmazon.
49 50 51 52 53 54 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 49 def initialize(orchestrator) @orchestrator = orchestrator @transport = Transport::HttpSellerApiConnection.new(profile: orchestrator.transporter_profile) @logger = Rails.logger @api_log = [] end |
Instance Attribute Details
#api_log ⇒ Object (readonly)
Returns the value of attribute api_log.
18 19 20 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 18 def api_log @api_log end |
#logger ⇒ Object (readonly)
Returns the value of attribute logger.
18 19 20 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 18 def logger @logger end |
#orchestrator ⇒ Object (readonly)
Returns the value of attribute orchestrator.
18 19 20 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 18 def orchestrator @orchestrator end |
#transport ⇒ Object (readonly)
Returns the value of attribute transport.
18 19 20 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 18 def transport @transport end |
Instance Method Details
#cancel_shipment(shipment_id) ⇒ CancelResult
PUT /shipping/v2/shipments/shipmentId/cancel
Cancels a purchased shipment
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 216 def cancel_shipment(shipment_id) url = build_url("/shipments/#{shipment_id}/cancel") logger.info("[AMZ-BS] Canceling shipment: #{shipment_id}") result = transport.send_data('', url, 'PUT', shipping_headers) if result[:success] log_api_call(operation: 'cancel_shipment', url: url, method: 'PUT', http_result: result[:http_result], success: true) CancelResult.new(success: true, error: nil) else error_msg = extract_error(result[:http_result]) log_api_call(operation: 'cancel_shipment', url: url, method: 'PUT', http_result: result[:http_result], success: false, error: error_msg) CancelResult.new(success: false, error: error_msg) end rescue StandardError => e logger.error("[AMZ-BS] cancel_shipment error: #{e.}") log_api_call(operation: 'cancel_shipment', url: url, method: 'PUT', success: false, error: e.) CancelResult.new(success: false, error: e.) end |
#get_rates(options) ⇒ RatesResult
POST /shipping/v2/shipments/rates
Returns eligible shipping service offerings for a shipment
For on-Amazon orders (channelType: AMAZON), shipTo is not required.
Amazon already knows the delivery address from the order.
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 68 def get_rates() url = build_url('/shipments/rates') payload = build_rates_payload() logger.info("[AMZ-BS] Requesting rates for order: #{[:amazon_order_id]}") logger.debug("[AMZ-BS] Rates payload: #{payload.to_json}") result = transport.send_data(payload.to_json, url, 'POST', shipping_headers) if result[:success] data = parse_response(result[:http_result]) payload_data = data[:payload] || data rates = normalize_rates(payload_data[:rates] || []) ineligible = payload_data[:ineligibleRates] || [] logger.info("[AMZ-BS] Got #{rates.size} eligible rate(s), #{ineligible.size} ineligible") log_api_call(operation: 'get_rates', url: url, method: 'POST', request_payload: payload.to_json, http_result: result[:http_result], success: true) RatesResult.new( success: true, request_token: payload_data[:requestToken], rates: rates, ineligible_rates: ineligible, error: nil ) else error_msg = extract_error(result[:http_result]) logger.error("[AMZ-BS] get_rates failed: #{error_msg}") log_api_call(operation: 'get_rates', url: url, method: 'POST', request_payload: payload.to_json, http_result: result[:http_result], success: false, error: error_msg) RatesResult.new(success: false, request_token: nil, rates: [], ineligible_rates: [], error: error_msg) end rescue StandardError => e logger.error("[AMZ-BS] get_rates error: #{e.}") logger.error("[AMZ-BS] Backtrace: #{e.backtrace.first(5).join("\n")}") log_api_call(operation: 'get_rates', url: url, method: 'POST', request_payload: payload&.to_json, success: false, error: e.) RatesResult.new(success: false, request_token: nil, rates: [], ineligible_rates: [], error: e.) end |
#get_shipment_documents(shipment_id, package_client_reference_id, format: 'PDF') ⇒ DocumentResult
GET /shipping/v2/shipments/shipmentId/documents
Returns shipping documents (label PDF) for a package
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 181 def get_shipment_documents(shipment_id, package_client_reference_id, format: 'PDF') url = build_url("/shipments/#{shipment_id}/documents") url += "?packageClientReferenceId=#{CGI.escape(package_client_reference_id)}&format=#{CGI.escape(format)}" logger.info("[AMZ-BS] Getting documents for shipment: #{shipment_id}") result = transport.send_data('', url, 'GET', shipping_headers) if result[:success] data = parse_response(result[:http_result]) payload_data = data[:payload] || data pkg_doc = payload_data.dig(:packageDocumentDetail, :packageDocuments) label_data = extract_label_from_documents(pkg_doc) log_api_call(operation: 'get_shipment_documents', url: url, method: 'GET', http_result: result[:http_result], success: true) DocumentResult.new(success: true, label_data: label_data, format: format, error: nil) else error_msg = extract_error(result[:http_result]) log_api_call(operation: 'get_shipment_documents', url: url, method: 'GET', http_result: result[:http_result], success: false, error: error_msg) DocumentResult.new(success: false, label_data: nil, format: nil, error: error_msg) end rescue StandardError => e logger.error("[AMZ-BS] get_shipment_documents error: #{e.}") log_api_call(operation: 'get_shipment_documents', url: url, method: 'GET', success: false, error: e.) DocumentResult.new(success: false, label_data: nil, format: nil, error: e.) end |
#get_tracking(tracking_id, carrier_id) ⇒ TrackingResult
GET /shipping/v2/tracking
Returns tracking information for a purchased shipment
246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 246 def get_tracking(tracking_id, carrier_id) url = build_url("/tracking?trackingId=#{CGI.escape(tracking_id)}&carrierId=#{CGI.escape(carrier_id)}") logger.info("[AMZ-BS] Getting tracking for: #{tracking_id}") result = transport.send_data('', url, 'GET', shipping_headers) if result[:success] data = parse_response(result[:http_result]) payload_data = data[:payload] || data log_api_call(operation: 'get_tracking', url: url, method: 'GET', http_result: result[:http_result], success: true) TrackingResult.new( success: true, tracking_id: payload_data[:trackingId], status: payload_data.dig(:summary, :status), event_history: payload_data[:eventHistory] || [], promised_delivery_date: payload_data[:promisedDeliveryDate], error: nil ) else error_msg = extract_error(result[:http_result]) log_api_call(operation: 'get_tracking', url: url, method: 'GET', http_result: result[:http_result], success: false, error: error_msg) TrackingResult.new(success: false, error: error_msg) end rescue StandardError => e logger.error("[AMZ-BS] get_tracking error: #{e.}") log_api_call(operation: 'get_tracking', url: url, method: 'GET', success: false, error: e.) TrackingResult.new(success: false, error: e.) end |
#purchase_shipment(options) ⇒ PurchaseResult
POST /shipping/v2/shipments
Purchases a shipping service and returns label documents
Must be called within 10 minutes of get_rates for EXTERNAL channel.
For AMAZON channel, the window may be longer but should still be prompt.
122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'app/services/edi/amazon/ship_with_amazon.rb', line 122 def purchase_shipment() url = build_url('/shipments') payload = build_purchase_payload() logger.info("[AMZ-BS] Purchasing shipment, rate_id: #{[:rate_id]}") logger.debug("[AMZ-BS] Purchase payload: #{payload.to_json}") result = transport.send_data(payload.to_json, url, 'POST', shipping_headers) if result[:success] data = parse_response(result[:http_result]) payload_data = data[:payload] || data shipment_id = payload_data[:shipmentId] promise = payload_data[:promise] pkg_detail = payload_data[:packageDocumentDetails]&.first || {} tracking_number = pkg_detail[:trackingId] label_data = extract_label_from_documents(pkg_detail[:packageDocuments]) logger.info("[AMZ-BS] Shipment purchased: #{shipment_id}, tracking: #{tracking_number}") log_api_call(operation: 'purchase_shipment', url: url, method: 'POST', request_payload: payload.to_json, http_result: result[:http_result], success: true) PurchaseResult.new( success: true, shipment_id: shipment_id, tracking_number: tracking_number, carrier_id: [:carrier_id], carrier_name: [:carrier_name], service_name: [:service_name], label_data: label_data, promise: promise, error: nil ) else error_msg = extract_error(result[:http_result]) logger.error("[AMZ-BS] purchase_shipment failed: #{error_msg}") log_api_call(operation: 'purchase_shipment', url: url, method: 'POST', request_payload: payload.to_json, http_result: result[:http_result], success: false, error: error_msg) PurchaseResult.new(success: false, error: error_msg) end rescue StandardError => e logger.error("[AMZ-BS] purchase_shipment error: #{e.}") logger.error("[AMZ-BS] Backtrace: #{e.backtrace.first(5).join("\n")}") log_api_call(operation: 'purchase_shipment', url: url, method: 'POST', request_payload: payload&.to_json, success: false, error: e.) PurchaseResult.new(success: false, error: e.) end |