Class: Portage::Ucp::Adapter Abstract
- Inherits:
-
Object
- Object
- Portage::Ucp::Adapter
- Defined in:
- lib/portage/ucp/adapter.rb
Overview
Subclass and override the methods for the capabilities you support. Unoverridden methods leave that capability out of the manifest.
Direct Known Subclasses
Instance Method Summary collapse
- #cancel_cart(cart_id:, idempotency_key:) ⇒ Portage::Ucp::Cart
- #cancel_checkout(checkout_id:, idempotency_key:) ⇒ Portage::Ucp::Checkout
-
#cancel_order(order_id:, idempotency_key:, reason: nil) ⇒ Portage::Ucp::Order
Cancels a placed order.
-
#complete_checkout(checkout_id:, payment_token:, idempotency_key:, mandate: nil) ⇒ Portage::Ucp::Checkout
Re-checks stock at the point of committing money, since search_catalog/ get_product (dev.ucp.shopping.catalog) don't promise live inventory and nothing else re-checks between browsing and buying.
-
#create_cart(line_items:, idempotency_key:, discount_codes: nil) ⇒ Portage::Ucp::Cart
discount_codes:is the dev.ucp.shopping.discount extension — nil (the default) means the request didn't touch discounts at all; an adapter that doesn't override #discount_codes_supported? never sees anything but nil here (see below). -
#create_checkout(line_items:, idempotency_key:, discount_codes: nil, fulfillment: nil) ⇒ Portage::Ucp::Checkout
--- Checkout (dev.ucp.shopping.checkout) ---
discount_codes:carries the same dev.ucp.shopping.discount semantics as create_cart/update_cart above. -
#create_payment_enrollment(idempotency_key:, mandate: nil) ⇒ Portage::Ucp::PaymentEnrollment
--- Payment Enrollment (app.portage-ucp.payment_enrollment — Portage extension, not part of the UCP spec) --- Starts a card-on-file enrollment.
- #delete_address(oauth_token:, address_id:, idempotency_key:) ⇒ Boolean
- #delete_payment_method(oauth_token:, payment_method_id:, idempotency_key:) ⇒ Boolean
-
#delete_shopper_data(oauth_token:, idempotency_key:) ⇒ Portage::Ucp::ShopperDataErasure
Erases every payment method, address, and linked identity Portage holds for this shopper.
-
#discount_codes_supported? ⇒ Boolean
--- Discount (dev.ucp.shopping.discount) --- Extends Cart/Checkout with the
discount_codes:param above rather than adding actions of its own — Capability::DISCOUNT advertises off this predicate instead of an overridden action method, since there's no dedicated method for #advertised_for? to detect an override on. -
#fulfillment_supported? ⇒ Boolean
--- Fulfillment (dev.ucp.shopping.fulfillment) --- Extends Checkout with the
fulfillment:param above rather than adding actions of its own — Capability::FULFILLMENT advertises off this predicate instead of an overridden action method, same rationale as #discount_codes_supported? above. -
#get_cart(cart_id:) ⇒ Portage::Ucp::Cart
--- Cart (dev.ucp.shopping.cart) --- Full-replacement semantics, matching UCP's real cart methods: create/ update take the complete desired line_items list, not a single item.
- #get_checkout(checkout_id:) ⇒ Portage::Ucp::Checkout
-
#get_order(order_id:) ⇒ Portage::Ucp::Order?
--- Order (dev.ucp.shopping.order) ---.
-
#get_payment_enrollment(enrollment_id:) ⇒ Portage::Ucp::PaymentEnrollment?
Nil if the enrollment isn't found.
-
#get_product(product_id:) ⇒ Portage::Ucp::ProductDetail?
nil when the product isn't found, same not-found posture as get_cart/get_checkout/get_order.
-
#link_identity(oauth_token:) ⇒ Portage::Ucp::Identity
--- Identity Linking (dev.ucp.shopping.identity, OAuth 2.0) ---.
- #list_addresses(oauth_token:) ⇒ Array<Portage::Ucp::SavedAddress>
- #list_payment_methods(oauth_token:) ⇒ Array<Portage::Ucp::PaymentMethodRef>
-
#lookup_catalog(product_ids:) ⇒ Portage::Ucp::CatalogSearchResult
Batch fetch — same result shape as search_catalog, for a caller that already has a set of product ids (e.g. hydrating a cart/order) and wants one round trip instead of N #get_product calls.
-
#refund_order(order_id:, line_items:, idempotency_key:, reason: nil) ⇒ Portage::Ucp::Order
Refunds one or more order line items.
-
#reorder(order_id:, idempotency_key:) ⇒ Portage::Ucp::ReorderResult?
--- Reorder (app.portage-ucp.reorder — Portage extension, not part of the UCP spec: dev.ucp.dev has no reorder/cart-hydration capability as of the 2026-04-08 spec) --- Hydrates a cart from a previous order's line items so a caller doesn't have to re-walk get_order + create_cart itself.
-
#request_return(order_id:, line_items:, idempotency_key:, reason: nil) ⇒ Portage::Ucp::Order
Requests a return for one or more order line items.
- #save_address(oauth_token:, address:, idempotency_key:) ⇒ Portage::Ucp::SavedAddress
-
#save_payment_method(oauth_token:, payment_token:, idempotency_key:) ⇒ Portage::Ucp::PaymentMethodRef
--- Payment Method / Saved Address / Shopper Data (app.portage-ucp.* — Portage extensions, not part of the UCP spec) ---
oauth_token:— notsubject:— is the authorization boundary on every method below, including the two list_* reads. -
#search_catalog(query:, limit:) ⇒ Portage::Ucp::CatalogSearchResult
--- Catalog (dev.ucp.shopping.catalog) ---.
- #update_cart(cart_id:, line_items:, idempotency_key:, discount_codes: nil) ⇒ Portage::Ucp::Cart
-
#update_checkout(checkout_id:, line_items:, idempotency_key:, discount_codes: nil, fulfillment: nil) ⇒ Portage::Ucp::Checkout
Full-replacement, same as update_cart — line_items is required on checkout update per the real spec.
Instance Method Details
#cancel_cart(cart_id:, idempotency_key:) ⇒ Portage::Ucp::Cart
38 |
# File 'lib/portage/ucp/adapter.rb', line 38 def cancel_cart(cart_id:, idempotency_key:) = not_implemented |
#cancel_checkout(checkout_id:, idempotency_key:) ⇒ Portage::Ucp::Checkout
85 |
# File 'lib/portage/ucp/adapter.rb', line 85 def cancel_checkout(checkout_id:, idempotency_key:) = not_implemented |
#cancel_order(order_id:, idempotency_key:, reason: nil) ⇒ Portage::Ucp::Order
Cancels a placed order. reason is an optional human-readable note,
not a closed enum — the platform-specific enum mapping (if any) is an
adapter concern.
94 95 96 97 98 99 100 |
# File 'lib/portage/ucp/adapter.rb', line 94 def cancel_order(order_id:, idempotency_key:, reason: nil) = not_implemented # Requests a return for one or more order line items. `line_items` is an # array of request-shaped hashes (`{id:, quantity:}`, unsigned) — same # request/response asymmetry as `create_cart`'s `line_items:`. A return # is a request the merchant still has to process; it shows up as a # `pending` `Portage::Ucp::Adjustment` until they do. # @return [Portage::Ucp::Order] |
#complete_checkout(checkout_id:, payment_token:, idempotency_key:, mandate: nil) ⇒ Portage::Ucp::Checkout
Re-checks stock at the point of committing money, since search_catalog/ get_product (dev.ucp.shopping.catalog) don't promise live inventory and nothing else re-checks between browsing and buying. An adapter whose platform rejects completion because a line item is out of stock or otherwise unavailable should raise Portage::Ucp::OutOfStockError rather than a generic/platform error, so callers can distinguish a stale-stock failure from e.g. a declined payment.
83 84 |
# File 'lib/portage/ucp/adapter.rb', line 83 def complete_checkout(checkout_id:, payment_token:, idempotency_key:, mandate: nil) = not_implemented # @return [Portage::Ucp::Checkout] |
#create_cart(line_items:, idempotency_key:, discount_codes: nil) ⇒ Portage::Ucp::Cart
discount_codes: is the dev.ucp.shopping.discount extension — nil
(the default) means the request didn't touch discounts at all; an
adapter that doesn't override #discount_codes_supported? never sees
anything but nil here (see below). Full-replacement like line_items:
once codes are involved, [] clears them, same as UCP's own
discounts_object semantics.
34 35 |
# File 'lib/portage/ucp/adapter.rb', line 34 def create_cart(line_items:, idempotency_key:, discount_codes: nil) = not_implemented # @return [Portage::Ucp::Cart] |
#create_checkout(line_items:, idempotency_key:, discount_codes: nil, fulfillment: nil) ⇒ Portage::Ucp::Checkout
--- Checkout (dev.ucp.shopping.checkout) ---
discount_codes: carries the same dev.ucp.shopping.discount
semantics as create_cart/update_cart above.
fulfillment: is the dev.ucp.shopping.fulfillment extension — nil
(the default) means the request didn't touch fulfillment at all; an
adapter that doesn't override #fulfillment_supported? never sees
anything but nil here (see below). On create it carries the agent's
desired methods (type + line_item_ids per shipping/pickup group —
Portage::Ucp::FulfillmentMethod#id/#destinations/#groups are
omitted since the merchant generates those); on update it carries the
agent's selected_destination_id/selected_option_id choices against
the methods/groups the merchant already returned.
53 54 |
# File 'lib/portage/ucp/adapter.rb', line 53 def create_checkout(line_items:, idempotency_key:, discount_codes: nil, fulfillment: nil) = not_implemented # @return [Portage::Ucp::Checkout] |
#create_payment_enrollment(idempotency_key:, mandate: nil) ⇒ Portage::Ucp::PaymentEnrollment
--- Payment Enrollment (app.portage-ucp.payment_enrollment — Portage
extension, not part of the UCP spec) ---
Starts a card-on-file enrollment. Card data never touches this
process: #create_payment_enrollment returns a setup_url for a
gateway-hosted page where the human enters their card, and the
caller polls #get_payment_enrollment until status leaves
"pending". See docs/plans/agentic-payments.md Phase 1.
152 153 |
# File 'lib/portage/ucp/adapter.rb', line 152 def create_payment_enrollment(idempotency_key:, mandate: nil) = not_implemented # @return [Portage::Ucp::PaymentEnrollment, nil] nil if the enrollment isn't found |
#delete_address(oauth_token:, address_id:, idempotency_key:) ⇒ Boolean
185 |
# File 'lib/portage/ucp/adapter.rb', line 185 def delete_address(oauth_token:, address_id:, idempotency_key:) = not_implemented |
#delete_payment_method(oauth_token:, payment_method_id:, idempotency_key:) ⇒ Boolean
178 |
# File 'lib/portage/ucp/adapter.rb', line 178 def delete_payment_method(oauth_token:, payment_method_id:, idempotency_key:) = not_implemented |
#delete_shopper_data(oauth_token:, idempotency_key:) ⇒ Portage::Ucp::ShopperDataErasure
Erases every payment method, address, and linked identity Portage holds for this shopper. Idempotent and safe to repeat: a second call on an already-erased subject returns zero counts, never raises. A real adapter must forward the deletion to the PSP/platform's own API — Portage core never holds the credential behind psp_reference, only the opaque reference itself.
194 |
# File 'lib/portage/ucp/adapter.rb', line 194 def delete_shopper_data(oauth_token:, idempotency_key:) = not_implemented |
#discount_codes_supported? ⇒ Boolean
--- Discount (dev.ucp.shopping.discount) ---
Extends Cart/Checkout with the discount_codes: param above rather
than adding actions of its own — Capability::DISCOUNT advertises off
this predicate instead of an overridden action method, since there's
no dedicated method for #advertised_for? to detect an override on.
125 |
# File 'lib/portage/ucp/adapter.rb', line 125 def discount_codes_supported? = false |
#fulfillment_supported? ⇒ Boolean
--- Fulfillment (dev.ucp.shopping.fulfillment) ---
Extends Checkout with the fulfillment: param above rather than
adding actions of its own — Capability::FULFILLMENT advertises off
this predicate instead of an overridden action method, same rationale
as #discount_codes_supported? above.
133 |
# File 'lib/portage/ucp/adapter.rb', line 133 def fulfillment_supported? = false |
#get_cart(cart_id:) ⇒ Portage::Ucp::Cart
--- Cart (dev.ucp.shopping.cart) ---
Full-replacement semantics, matching UCP's real cart methods: create/
update take the complete desired line_items list, not a single item.
line_items: is an array of request-shaped hashes (e.g.
{product_id:, quantity:}) — the adapter looks up product data and
builds the response's Item/Total/LineItem itself.
26 27 28 29 30 31 32 33 |
# File 'lib/portage/ucp/adapter.rb', line 26 def get_cart(cart_id:) = not_implemented # `discount_codes:` is the dev.ucp.shopping.discount extension — nil # (the default) means the request didn't touch discounts at all; an # adapter that doesn't override #discount_codes_supported? never sees # anything but nil here (see below). Full-replacement like line_items: # once codes are involved, [] clears them, same as UCP's own # discounts_object semantics. # @return [Portage::Ucp::Cart] |
#get_checkout(checkout_id:) ⇒ Portage::Ucp::Checkout
55 |
# File 'lib/portage/ucp/adapter.rb', line 55 def get_checkout(checkout_id:) = not_implemented |
#get_order(order_id:) ⇒ Portage::Ucp::Order?
--- Order (dev.ucp.shopping.order) ---
89 90 91 92 93 |
# File 'lib/portage/ucp/adapter.rb', line 89 def get_order(order_id:) = not_implemented # Cancels a placed order. `reason` is an optional human-readable note, # not a closed enum — the platform-specific enum mapping (if any) is an # adapter concern. # @return [Portage::Ucp::Order] |
#get_payment_enrollment(enrollment_id:) ⇒ Portage::Ucp::PaymentEnrollment?
Returns nil if the enrollment isn't found.
154 |
# File 'lib/portage/ucp/adapter.rb', line 154 def get_payment_enrollment(enrollment_id:) = not_implemented |
#get_product(product_id:) ⇒ Portage::Ucp::ProductDetail?
nil when the product isn't found, same not-found posture as get_cart/get_checkout/get_order.
12 13 14 15 16 |
# File 'lib/portage/ucp/adapter.rb', line 12 def get_product(product_id:) = not_implemented # Batch fetch — same result shape as search_catalog, for a caller that # already has a set of product ids (e.g. hydrating a cart/order) and # wants one round trip instead of N #get_product calls. # @return [Portage::Ucp::CatalogSearchResult] |
#link_identity(oauth_token:) ⇒ Portage::Ucp::Identity
--- Identity Linking (dev.ucp.shopping.identity, OAuth 2.0) ---
137 |
# File 'lib/portage/ucp/adapter.rb', line 137 def link_identity(oauth_token:) = not_implemented |
#list_addresses(oauth_token:) ⇒ Array<Portage::Ucp::SavedAddress>
183 184 |
# File 'lib/portage/ucp/adapter.rb', line 183 def list_addresses(oauth_token:) = not_implemented # @return [Boolean] |
#list_payment_methods(oauth_token:) ⇒ Array<Portage::Ucp::PaymentMethodRef>
176 177 |
# File 'lib/portage/ucp/adapter.rb', line 176 def list_payment_methods(oauth_token:) = not_implemented # @return [Boolean] |
#lookup_catalog(product_ids:) ⇒ Portage::Ucp::CatalogSearchResult
Batch fetch — same result shape as search_catalog, for a caller that already has a set of product ids (e.g. hydrating a cart/order) and wants one round trip instead of N #get_product calls.
17 |
# File 'lib/portage/ucp/adapter.rb', line 17 def lookup_catalog(product_ids:) = not_implemented |
#refund_order(order_id:, line_items:, idempotency_key:, reason: nil) ⇒ Portage::Ucp::Order
Refunds one or more order line items.
104 |
# File 'lib/portage/ucp/adapter.rb', line 104 def refund_order(order_id:, line_items:, idempotency_key:, reason: nil) = not_implemented |
#reorder(order_id:, idempotency_key:) ⇒ Portage::Ucp::ReorderResult?
--- Reorder (app.portage-ucp.reorder — Portage extension, not part of the UCP spec: dev.ucp.dev has no reorder/cart-hydration capability as of the 2026-04-08 spec) --- Hydrates a cart from a previous order's line items so a caller doesn't have to re-walk get_order + create_cart itself. Re-checks each item's current price/availability rather than replaying the order's snapshot totals — order_line_item.json's totals are historical, not live, same posture as complete_checkout's stock re-check above — and drops anything no longer purchasable instead of failing the whole call, reporting what got dropped via ReorderResult#unavailable_items.
117 |
# File 'lib/portage/ucp/adapter.rb', line 117 def reorder(order_id:, idempotency_key:) = not_implemented |
#request_return(order_id:, line_items:, idempotency_key:, reason: nil) ⇒ Portage::Ucp::Order
Requests a return for one or more order line items. line_items is an
array of request-shaped hashes ({id:, quantity:}, unsigned) — same
request/response asymmetry as create_cart's line_items:. A return
is a request the merchant still has to process; it shows up as a
pending Portage::Ucp::Adjustment until they do.
101 102 103 |
# File 'lib/portage/ucp/adapter.rb', line 101 def request_return(order_id:, line_items:, idempotency_key:, reason: nil) = not_implemented # Refunds one or more order line items. # @return [Portage::Ucp::Order] |
#save_address(oauth_token:, address:, idempotency_key:) ⇒ Portage::Ucp::SavedAddress
181 182 |
# File 'lib/portage/ucp/adapter.rb', line 181 def save_address(oauth_token:, address:, idempotency_key:) = not_implemented # @return [Array<Portage::Ucp::SavedAddress>] |
#save_payment_method(oauth_token:, payment_token:, idempotency_key:) ⇒ Portage::Ucp::PaymentMethodRef
--- Payment Method / Saved Address / Shopper Data (app.portage-ucp.*
— Portage extensions, not part of the UCP spec) ---
oauth_token: — not subject: — is the authorization boundary on
every method below, including the two list_* reads. Mcp::Server
treats a call as mutating only when it takes an idempotency_key
(mcp/server.rb:38), and both the authorize and rate_limit guards
skip non-mutating calls entirely. A bare subject: string would let
any caller who knows (or guesses) a subject enumerate another
shopper's saved payment references and addresses — the exact risk
§16 (design-log.md:816-819) calls out lookups against this data for.
Carrying the credential on every call closes that: possession of a
subject grants nothing without a valid oauth_token to derive it from.
Do not "simplify" this back to subject:.
174 175 |
# File 'lib/portage/ucp/adapter.rb', line 174 def save_payment_method(oauth_token:, payment_token:, idempotency_key:) = not_implemented # @return [Array<Portage::Ucp::PaymentMethodRef>] |
#search_catalog(query:, limit:) ⇒ Portage::Ucp::CatalogSearchResult
--- Catalog (dev.ucp.shopping.catalog) ---
8 9 10 11 |
# File 'lib/portage/ucp/adapter.rb', line 8 def search_catalog(query:, limit:) = not_implemented # nil when the product isn't found, same not-found posture as # get_cart/get_checkout/get_order. # @return [Portage::Ucp::ProductDetail, nil] |
#update_cart(cart_id:, line_items:, idempotency_key:, discount_codes: nil) ⇒ Portage::Ucp::Cart
36 37 |
# File 'lib/portage/ucp/adapter.rb', line 36 def update_cart(cart_id:, line_items:, idempotency_key:, discount_codes: nil) = not_implemented # @return [Portage::Ucp::Cart] |
#update_checkout(checkout_id:, line_items:, idempotency_key:, discount_codes: nil, fulfillment: nil) ⇒ Portage::Ucp::Checkout
Full-replacement, same as update_cart — line_items is required on checkout update per the real spec.
60 61 62 |
# File 'lib/portage/ucp/adapter.rb', line 60 def update_checkout(checkout_id:, line_items:, idempotency_key:, discount_codes: nil, fulfillment: nil) not_implemented end |