Add payment dispute management methods and tests in FulfillmentClient
This commit is contained in:
parent
1f06ec9e44
commit
f30b31ec00
3 changed files with 411 additions and 3 deletions
|
|
@ -45,6 +45,9 @@ class ApiTransport:
|
||||||
filtered_params = None
|
filtered_params = None
|
||||||
if params is not None:
|
if params is not None:
|
||||||
filtered_params = {key: value for key, value in params.items() if value is not None}
|
filtered_params = {key: value for key, value in params.items() if value is not None}
|
||||||
|
if path.startswith(("http://", "https://")):
|
||||||
|
url = path
|
||||||
|
else:
|
||||||
url = f"{self.base_url}{path}"
|
url = f"{self.base_url}{path}"
|
||||||
try:
|
try:
|
||||||
with httpx.Client(timeout=self.timeout_seconds) as client:
|
with httpx.Client(timeout=self.timeout_seconds) as client:
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,77 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from urllib.parse import urlparse
|
||||||
|
|
||||||
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from ebay_client.core.http.transport import ApiTransport
|
from ebay_client.core.http.transport import ApiTransport
|
||||||
from ebay_client.generated.fulfillment.models import (
|
from ebay_client.generated.fulfillment.models import (
|
||||||
|
AcceptPaymentDisputeRequest,
|
||||||
|
AddEvidencePaymentDisputeRequest,
|
||||||
|
AddEvidencePaymentDisputeResponse,
|
||||||
|
ContestPaymentDisputeRequest,
|
||||||
|
DisputeSummaryResponse,
|
||||||
|
FileEvidence,
|
||||||
|
IssueRefundRequest,
|
||||||
Order,
|
Order,
|
||||||
OrderSearchPagedCollection,
|
OrderSearchPagedCollection,
|
||||||
|
PaymentDispute,
|
||||||
|
PaymentDisputeActivityHistory,
|
||||||
|
Refund,
|
||||||
ShippingFulfillment,
|
ShippingFulfillment,
|
||||||
|
ShippingFulfillmentDetails,
|
||||||
ShippingFulfillmentPagedCollection,
|
ShippingFulfillmentPagedCollection,
|
||||||
|
UpdateEvidencePaymentDisputeRequest,
|
||||||
)
|
)
|
||||||
|
|
||||||
FULFILLMENT_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.fulfillment"
|
FULFILLMENT_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.fulfillment"
|
||||||
FULFILLMENT_READ_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly"
|
FULFILLMENT_READ_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly"
|
||||||
FULFILLMENT_READ_SCOPE_OPTIONS = [[FULFILLMENT_READ_SCOPE], [FULFILLMENT_SCOPE]]
|
FULFILLMENT_READ_SCOPE_OPTIONS = [[FULFILLMENT_READ_SCOPE], [FULFILLMENT_SCOPE]]
|
||||||
|
FINANCES_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.finances"
|
||||||
|
PAYMENT_DISPUTE_SCOPE = "https://api.ebay.com/oauth/api_scope/sell.payment.dispute"
|
||||||
|
PAYMENT_DISPUTE_BASE_URL = "https://apiz.ebay.com/sell/fulfillment/v1"
|
||||||
|
|
||||||
|
|
||||||
|
class CreatedShippingFulfillment(BaseModel):
|
||||||
|
location: str | None = None
|
||||||
|
fulfillment_id: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
class EvidenceFileDownload(BaseModel):
|
||||||
|
content: bytes
|
||||||
|
content_disposition: str | None = None
|
||||||
|
file_name: str | None = None
|
||||||
|
|
||||||
|
|
||||||
|
def extract_fulfillment_id(location: str | None) -> str | None:
|
||||||
|
if not location:
|
||||||
|
return None
|
||||||
|
|
||||||
|
path = urlparse(location).path.rstrip("/")
|
||||||
|
if not path:
|
||||||
|
return None
|
||||||
|
|
||||||
|
_, _, resource_id = path.rpartition("/")
|
||||||
|
return resource_id or None
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_file_name(content_disposition: str | None) -> str | None:
|
||||||
|
if not content_disposition or "filename=" not in content_disposition:
|
||||||
|
return None
|
||||||
|
|
||||||
|
value = content_disposition.split("filename=", 1)[1].strip()
|
||||||
|
if value.startswith('"') and value.endswith('"'):
|
||||||
|
return value[1:-1]
|
||||||
|
return value
|
||||||
|
|
||||||
|
|
||||||
class FulfillmentClient:
|
class FulfillmentClient:
|
||||||
def __init__(self, transport: ApiTransport) -> None:
|
def __init__(self, transport: ApiTransport) -> None:
|
||||||
self.transport = transport
|
self.transport = transport
|
||||||
|
|
||||||
|
def _dispute_path(self, path: str) -> str:
|
||||||
|
return f"{PAYMENT_DISPUTE_BASE_URL}{path}"
|
||||||
|
|
||||||
def get_order(self, order_id: str) -> Order:
|
def get_order(self, order_id: str) -> Order:
|
||||||
return self.transport.request_model(
|
return self.transport.request_model(
|
||||||
Order,
|
Order,
|
||||||
|
|
@ -34,6 +89,16 @@ class FulfillmentClient:
|
||||||
params={"limit": limit, "offset": offset},
|
params={"limit": limit, "offset": offset},
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def issue_refund(self, order_id: str, payload: IssueRefundRequest) -> Refund:
|
||||||
|
return self.transport.request_model(
|
||||||
|
Refund,
|
||||||
|
"POST",
|
||||||
|
f"/sell/fulfillment/v1/order/{order_id}/issue_refund",
|
||||||
|
scopes=[FINANCES_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
def get_shipping_fulfillments(self, order_id: str) -> ShippingFulfillmentPagedCollection:
|
def get_shipping_fulfillments(self, order_id: str) -> ShippingFulfillmentPagedCollection:
|
||||||
return self.transport.request_model(
|
return self.transport.request_model(
|
||||||
ShippingFulfillmentPagedCollection,
|
ShippingFulfillmentPagedCollection,
|
||||||
|
|
@ -49,3 +114,153 @@ class FulfillmentClient:
|
||||||
f"/sell/fulfillment/v1/order/{order_id}/shipping_fulfillment/{fulfillment_id}",
|
f"/sell/fulfillment/v1/order/{order_id}/shipping_fulfillment/{fulfillment_id}",
|
||||||
scope_options=FULFILLMENT_READ_SCOPE_OPTIONS,
|
scope_options=FULFILLMENT_READ_SCOPE_OPTIONS,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def create_shipping_fulfillment(
|
||||||
|
self,
|
||||||
|
order_id: str,
|
||||||
|
payload: ShippingFulfillmentDetails,
|
||||||
|
) -> CreatedShippingFulfillment:
|
||||||
|
response = self.transport.request(
|
||||||
|
"POST",
|
||||||
|
f"/sell/fulfillment/v1/order/{order_id}/shipping_fulfillment",
|
||||||
|
scopes=[FULFILLMENT_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
location = response.headers.get("Location")
|
||||||
|
return CreatedShippingFulfillment(location=location, fulfillment_id=extract_fulfillment_id(location))
|
||||||
|
|
||||||
|
def get_payment_dispute(self, payment_dispute_id: str) -> PaymentDispute:
|
||||||
|
return self.transport.request_model(
|
||||||
|
PaymentDispute,
|
||||||
|
"GET",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def fetch_evidence_content(
|
||||||
|
self,
|
||||||
|
payment_dispute_id: str,
|
||||||
|
*,
|
||||||
|
evidence_id: str,
|
||||||
|
file_id: str,
|
||||||
|
) -> EvidenceFileDownload:
|
||||||
|
response = self.transport.request(
|
||||||
|
"GET",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}/fetch_evidence_content"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
params={"evidence_id": evidence_id, "file_id": file_id},
|
||||||
|
)
|
||||||
|
content_disposition = response.headers.get("content-disposition")
|
||||||
|
return EvidenceFileDownload(
|
||||||
|
content=response.content,
|
||||||
|
content_disposition=content_disposition,
|
||||||
|
file_name=_extract_file_name(content_disposition),
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_payment_dispute_activities(self, payment_dispute_id: str) -> PaymentDisputeActivityHistory:
|
||||||
|
return self.transport.request_model(
|
||||||
|
PaymentDisputeActivityHistory,
|
||||||
|
"GET",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}/activity"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_payment_dispute_summaries(
|
||||||
|
self,
|
||||||
|
*,
|
||||||
|
order_id: str | None = None,
|
||||||
|
buyer_username: str | None = None,
|
||||||
|
open_date_from: str | None = None,
|
||||||
|
open_date_to: str | None = None,
|
||||||
|
payment_dispute_status: list[str] | None = None,
|
||||||
|
limit: int | None = None,
|
||||||
|
offset: int | None = None,
|
||||||
|
) -> DisputeSummaryResponse:
|
||||||
|
params: dict[str, object] = {
|
||||||
|
"order_id": order_id,
|
||||||
|
"buyer_username": buyer_username,
|
||||||
|
"open_date_from": open_date_from,
|
||||||
|
"open_date_to": open_date_to,
|
||||||
|
"limit": limit,
|
||||||
|
"offset": offset,
|
||||||
|
}
|
||||||
|
if payment_dispute_status is not None:
|
||||||
|
params["payment_dispute_status"] = payment_dispute_status
|
||||||
|
|
||||||
|
return self.transport.request_model(
|
||||||
|
DisputeSummaryResponse,
|
||||||
|
"GET",
|
||||||
|
self._dispute_path("/payment_dispute_summary"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
params=params,
|
||||||
|
)
|
||||||
|
|
||||||
|
def contest_payment_dispute(
|
||||||
|
self,
|
||||||
|
payment_dispute_id: str,
|
||||||
|
payload: ContestPaymentDisputeRequest,
|
||||||
|
) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"POST",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}/contest"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
def accept_payment_dispute(
|
||||||
|
self,
|
||||||
|
payment_dispute_id: str,
|
||||||
|
payload: AcceptPaymentDisputeRequest,
|
||||||
|
) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"POST",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}/accept"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
def upload_evidence_file(
|
||||||
|
self,
|
||||||
|
payment_dispute_id: str,
|
||||||
|
*,
|
||||||
|
file_name: str,
|
||||||
|
content: bytes,
|
||||||
|
content_type: str = "image/jpeg",
|
||||||
|
) -> FileEvidence:
|
||||||
|
return self.transport.request_model(
|
||||||
|
FileEvidence,
|
||||||
|
"POST",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}/upload_evidence_file"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
files={"file": (file_name, content, content_type)},
|
||||||
|
)
|
||||||
|
|
||||||
|
def add_evidence(
|
||||||
|
self,
|
||||||
|
payment_dispute_id: str,
|
||||||
|
payload: AddEvidencePaymentDisputeRequest,
|
||||||
|
) -> AddEvidencePaymentDisputeResponse:
|
||||||
|
return self.transport.request_model(
|
||||||
|
AddEvidencePaymentDisputeResponse,
|
||||||
|
"POST",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}/add_evidence"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
||||||
|
def update_evidence(
|
||||||
|
self,
|
||||||
|
payment_dispute_id: str,
|
||||||
|
payload: UpdateEvidencePaymentDisputeRequest,
|
||||||
|
) -> None:
|
||||||
|
self.transport.request_json(
|
||||||
|
"POST",
|
||||||
|
self._dispute_path(f"/payment_dispute/{payment_dispute_id}/update_evidence"),
|
||||||
|
scopes=[PAYMENT_DISPUTE_SCOPE],
|
||||||
|
headers={"Content-Type": "application/json"},
|
||||||
|
json_body=payload.model_dump(by_alias=True, exclude_none=True),
|
||||||
|
)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ from ebay_client.account.client import AccountClient
|
||||||
from ebay_client.core.auth.models import OAuthToken
|
from ebay_client.core.auth.models import OAuthToken
|
||||||
from ebay_client.core.http.transport import ApiTransport
|
from ebay_client.core.http.transport import ApiTransport
|
||||||
from ebay_client.feed.client import CreatedFeedResource, FeedClient, FeedFileDownload
|
from ebay_client.feed.client import CreatedFeedResource, FeedClient, FeedFileDownload
|
||||||
from ebay_client.fulfillment.client import FulfillmentClient
|
from ebay_client.fulfillment.client import CreatedShippingFulfillment, EvidenceFileDownload, FulfillmentClient
|
||||||
from ebay_client.generated.account.models import (
|
from ebay_client.generated.account.models import (
|
||||||
FulfillmentPolicy,
|
FulfillmentPolicy,
|
||||||
FulfillmentPolicyRequest,
|
FulfillmentPolicyRequest,
|
||||||
|
|
@ -26,7 +26,23 @@ from ebay_client.generated.feed.models import (
|
||||||
UpdateUserScheduleRequest,
|
UpdateUserScheduleRequest,
|
||||||
UserScheduleResponse,
|
UserScheduleResponse,
|
||||||
)
|
)
|
||||||
from ebay_client.generated.fulfillment.models import Order
|
from ebay_client.generated.fulfillment.models import (
|
||||||
|
AcceptPaymentDisputeRequest,
|
||||||
|
AddEvidencePaymentDisputeRequest,
|
||||||
|
AddEvidencePaymentDisputeResponse,
|
||||||
|
FileEvidence,
|
||||||
|
IssueRefundRequest,
|
||||||
|
Order,
|
||||||
|
OrderLineItems,
|
||||||
|
PaymentDispute,
|
||||||
|
PaymentDisputeActivityHistory,
|
||||||
|
Refund,
|
||||||
|
ShippingFulfillmentDetails,
|
||||||
|
LineItemReference,
|
||||||
|
ContestPaymentDisputeRequest,
|
||||||
|
DisputeSummaryResponse,
|
||||||
|
UpdateEvidencePaymentDisputeRequest,
|
||||||
|
)
|
||||||
from ebay_client.generated.inventory.models import (
|
from ebay_client.generated.inventory.models import (
|
||||||
Address,
|
Address,
|
||||||
BaseResponse,
|
BaseResponse,
|
||||||
|
|
@ -517,6 +533,180 @@ def test_fulfillment_wrapper_returns_order_model(httpx_mock: HTTPXMock) -> None:
|
||||||
assert result.orderId == "ORDER-1"
|
assert result.orderId == "ORDER-1"
|
||||||
|
|
||||||
|
|
||||||
|
def test_fulfillment_wrapper_supports_refund_and_shipping_creation(httpx_mock: HTTPXMock) -> None:
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://api.ebay.com/sell/fulfillment/v1/order/ORDER-1/issue_refund",
|
||||||
|
json={"refundId": "REFUND-1", "refundStatus": "PENDING"},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://api.ebay.com/sell/fulfillment/v1/order/ORDER-1/shipping_fulfillment",
|
||||||
|
status_code=201,
|
||||||
|
headers={
|
||||||
|
"Location": "https://api.ebay.com/sell/fulfillment/v1/order/ORDER-1/shipping_fulfillment/FULFILL-1"
|
||||||
|
},
|
||||||
|
json={},
|
||||||
|
)
|
||||||
|
|
||||||
|
client = FulfillmentClient(build_transport())
|
||||||
|
refund = client.issue_refund(
|
||||||
|
"ORDER-1",
|
||||||
|
IssueRefundRequest(reasonForRefund="BUYER_CANCELLED", comment="requested", orderLevelRefundAmount={"currency": "USD", "value": "10.00"}),
|
||||||
|
)
|
||||||
|
fulfillment = client.create_shipping_fulfillment(
|
||||||
|
"ORDER-1",
|
||||||
|
ShippingFulfillmentDetails(
|
||||||
|
lineItems=[LineItemReference(lineItemId="LINE-1", quantity=1)],
|
||||||
|
shippingCarrierCode="USPS",
|
||||||
|
trackingNumber="TRACK123",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
assert isinstance(refund, Refund)
|
||||||
|
assert refund.refundId == "REFUND-1"
|
||||||
|
assert isinstance(fulfillment, CreatedShippingFulfillment)
|
||||||
|
assert fulfillment.fulfillment_id == "FULFILL-1"
|
||||||
|
|
||||||
|
refund_request = httpx_mock.get_requests()[0]
|
||||||
|
refund_body = json.loads(refund_request.content.decode("utf-8"))
|
||||||
|
assert refund_body["reasonForRefund"] == "BUYER_CANCELLED"
|
||||||
|
assert refund_request.headers["Authorization"] == "Bearer test-token"
|
||||||
|
|
||||||
|
shipping_request = httpx_mock.get_requests()[1]
|
||||||
|
shipping_body = json.loads(shipping_request.content.decode("utf-8"))
|
||||||
|
assert shipping_body["shippingCarrierCode"] == "USPS"
|
||||||
|
assert shipping_body["trackingNumber"] == "TRACK123"
|
||||||
|
|
||||||
|
|
||||||
|
def test_fulfillment_wrapper_supports_payment_dispute_workflow(httpx_mock: HTTPXMock) -> None:
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1",
|
||||||
|
json={"paymentDisputeId": "DISPUTE-1", "revision": 3},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1/activity",
|
||||||
|
json={"activities": []},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url=(
|
||||||
|
"https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute_summary?buyer_username=buyer1"
|
||||||
|
"&payment_dispute_status=OPEN&payment_dispute_status=ACTION_NEEDED&limit=25&offset=0"
|
||||||
|
),
|
||||||
|
json={"paymentDisputeSummaries": [], "total": 0},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1/contest",
|
||||||
|
status_code=204,
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1/accept",
|
||||||
|
status_code=204,
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1/upload_evidence_file",
|
||||||
|
json={"fileId": "FILE-1", "fileName": "label.jpg"},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1/add_evidence",
|
||||||
|
json={"evidenceId": "EVID-1"},
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="POST",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1/update_evidence",
|
||||||
|
status_code=204,
|
||||||
|
)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url="https://apiz.ebay.com/sell/fulfillment/v1/payment_dispute/DISPUTE-1/fetch_evidence_content?evidence_id=EVID-1&file_id=FILE-1",
|
||||||
|
status_code=200,
|
||||||
|
headers={"content-disposition": 'attachment; filename="label.jpg"'},
|
||||||
|
content=b"binary-evidence",
|
||||||
|
)
|
||||||
|
|
||||||
|
client = FulfillmentClient(build_transport())
|
||||||
|
dispute = client.get_payment_dispute("DISPUTE-1")
|
||||||
|
activities = client.get_payment_dispute_activities("DISPUTE-1")
|
||||||
|
summaries = client.get_payment_dispute_summaries(
|
||||||
|
buyer_username="buyer1",
|
||||||
|
payment_dispute_status=["OPEN", "ACTION_NEEDED"],
|
||||||
|
limit=25,
|
||||||
|
offset=0,
|
||||||
|
)
|
||||||
|
client.contest_payment_dispute("DISPUTE-1", ContestPaymentDisputeRequest(revision=3, note="tracking attached"))
|
||||||
|
client.accept_payment_dispute("DISPUTE-1", AcceptPaymentDisputeRequest(revision=3))
|
||||||
|
file_evidence = client.upload_evidence_file(
|
||||||
|
"DISPUTE-1",
|
||||||
|
file_name="label.jpg",
|
||||||
|
content=b"jpg-bytes",
|
||||||
|
content_type="image/jpeg",
|
||||||
|
)
|
||||||
|
evidence_response = client.add_evidence(
|
||||||
|
"DISPUTE-1",
|
||||||
|
AddEvidencePaymentDisputeRequest(
|
||||||
|
evidenceType="PROOF_OF_DELIVERY",
|
||||||
|
files=[FileEvidence(fileId="FILE-1")],
|
||||||
|
lineItems=[OrderLineItems(lineItemId="LINE-1", itemId="ITEM-1")],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
client.update_evidence(
|
||||||
|
"DISPUTE-1",
|
||||||
|
UpdateEvidencePaymentDisputeRequest(
|
||||||
|
evidenceId="EVID-1",
|
||||||
|
evidenceType="PROOF_OF_DELIVERY",
|
||||||
|
files=[FileEvidence(fileId="FILE-1")],
|
||||||
|
lineItems=[OrderLineItems(lineItemId="LINE-1", itemId="ITEM-1")],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
evidence_file = client.fetch_evidence_content("DISPUTE-1", evidence_id="EVID-1", file_id="FILE-1")
|
||||||
|
|
||||||
|
assert isinstance(dispute, PaymentDispute)
|
||||||
|
assert isinstance(activities, PaymentDisputeActivityHistory)
|
||||||
|
assert isinstance(summaries, DisputeSummaryResponse)
|
||||||
|
assert summaries.total == 0
|
||||||
|
assert isinstance(file_evidence, FileEvidence)
|
||||||
|
assert file_evidence.fileId == "FILE-1"
|
||||||
|
assert isinstance(evidence_response, AddEvidencePaymentDisputeResponse)
|
||||||
|
assert evidence_response.evidenceId == "EVID-1"
|
||||||
|
assert isinstance(evidence_file, EvidenceFileDownload)
|
||||||
|
assert evidence_file.file_name == "label.jpg"
|
||||||
|
assert evidence_file.content == b"binary-evidence"
|
||||||
|
|
||||||
|
contest_request = httpx_mock.get_requests()[3]
|
||||||
|
assert contest_request.url.host == "apiz.ebay.com"
|
||||||
|
contest_body = json.loads(contest_request.content.decode("utf-8"))
|
||||||
|
assert contest_body["revision"] == 3
|
||||||
|
|
||||||
|
upload_request = httpx_mock.get_requests()[5]
|
||||||
|
assert upload_request.url.host == "apiz.ebay.com"
|
||||||
|
assert upload_request.headers["Content-Type"].startswith("multipart/form-data;")
|
||||||
|
|
||||||
|
|
||||||
|
def test_fulfillment_wrapper_accepts_readonly_or_full_scope_options_for_shipping_reads(httpx_mock: HTTPXMock) -> None:
|
||||||
|
oauth_client = RecordingOAuthClient()
|
||||||
|
transport = ApiTransport(base_url="https://api.ebay.com", oauth_client=oauth_client)
|
||||||
|
httpx_mock.add_response(
|
||||||
|
method="GET",
|
||||||
|
url="https://api.ebay.com/sell/fulfillment/v1/order/ORDER-1/shipping_fulfillment/FULL-1",
|
||||||
|
json={"fulfillmentId": "FULL-1"},
|
||||||
|
)
|
||||||
|
|
||||||
|
FulfillmentClient(transport).get_shipping_fulfillment("ORDER-1", "FULL-1")
|
||||||
|
|
||||||
|
assert oauth_client.last_scopes is None
|
||||||
|
assert oauth_client.last_scope_options == [
|
||||||
|
["https://api.ebay.com/oauth/api_scope/sell.fulfillment.readonly"],
|
||||||
|
["https://api.ebay.com/oauth/api_scope/sell.fulfillment"],
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
def test_account_wrapper_returns_programs_model(httpx_mock: HTTPXMock) -> None:
|
def test_account_wrapper_returns_programs_model(httpx_mock: HTTPXMock) -> None:
|
||||||
httpx_mock.add_response(
|
httpx_mock.add_response(
|
||||||
method="GET",
|
method="GET",
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue