Add payment dispute management methods and tests in FulfillmentClient

This commit is contained in:
claudi 2026-04-07 11:07:17 +02:00
parent 1f06ec9e44
commit f30b31ec00
3 changed files with 411 additions and 3 deletions

View file

@ -8,7 +8,7 @@ from ebay_client.account.client import AccountClient
from ebay_client.core.auth.models import OAuthToken
from ebay_client.core.http.transport import ApiTransport
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 (
FulfillmentPolicy,
FulfillmentPolicyRequest,
@ -26,7 +26,23 @@ from ebay_client.generated.feed.models import (
UpdateUserScheduleRequest,
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 (
Address,
BaseResponse,
@ -517,6 +533,180 @@ def test_fulfillment_wrapper_returns_order_model(httpx_mock: HTTPXMock) -> None:
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:
httpx_mock.add_response(
method="GET",