2026-07-30 18:01:36 +08:00
param (
[ string ] $AuthUrl = 'http://127.0.0.1:9502' ,
[ string ] $IamUrl = 'http://127.0.0.1:9501' ,
[ string ] $FileUrl = 'http://127.0.0.1:9506' ,
[ string ] $OrderUrl = 'http://127.0.0.1:9504' ,
2026-08-04 09:05:28 +08:00
[ string ] $WarehouseUrl = 'http://127.0.0.1:9505' ,
2026-07-30 18:01:36 +08:00
[ string ] $ImagePath ,
2026-08-04 17:18:07 +08:00
[ long ] $PublicReceivingWarehouseId = 0 ,
[ long ] $PublicDestinationWarehouseId = 0 ,
2026-07-30 18:01:36 +08:00
[ string ] $PlatformPassword = 'ChangeMe123!'
)
$ErrorActionPreference = 'Stop'
2026-08-04 09:05:28 +08:00
function Invoke-Json($method , $url , $body = $null , $token = $null , $extraHeaders = @ {}) {
2026-07-30 18:01:36 +08:00
$headers = @ {}; if ( $token ) { $headers . Authorization = "Bearer $token " }
2026-08-04 09:05:28 +08:00
foreach ( $name in $extraHeaders . Keys ) { $headers [ $name ] = $extraHeaders [ $name ] }
2026-07-30 18:01:36 +08:00
$args = @ { Method = $method ; Uri = $url ; Headers = $headers ; TimeoutSec = 20 }
if ( $null -ne $body ) { $args . ContentType = 'application/json' ; $args . Body = $body | ConvertTo-Json -Depth 12 }
Invoke-RestMethod @args
}
function Invoke-Status($method , $url , $body , $token ) {
try { Invoke-Json $method $url $body $token | Out-Null ; return 200 }
catch { return [ int ] $_ . Exception . Response . StatusCode }
}
function Assert-Equal($name , $actual , $expected ) {
if ( $actual -ne $expected ) { throw " $name expected= $expected actual= $actual " }
}
2026-08-04 09:05:28 +08:00
function Assert-Success($name , $response ) {
if ( $null -eq $response -or [ string ] $response . code -notin @ ( '200' , '0' )) {
throw " $name failed code= $( $response . code ) message= $( $response . message ) "
}
}
2026-07-30 18:01:36 +08:00
$stamp = Get-Date -Format 'MMddHHmmss'
$username = "phase1_ $stamp " ; $password = 'Phase1Test123!'
$registration = Invoke-Json POST " $AuthUrl /api/auth/register" @ {
username = $username ; password = $password ; displayName = 'Phase One Customer' ; companyName = "Phase One Enterprise $stamp "
legalPerson = 'Test Legal Person' ; legalPhone = '13800138000' ; contactEmail = "phase1_ $stamp @example.com"
countryCode = 'CN' ; address = 'No. 1 Test Road, Nanshan, Shenzhen'
}
Assert-Equal 'registration status' $registration . data . tenantStatus 'PROFILE_REQUIRED'
2026-08-04 09:05:28 +08:00
Assert-Equal 'registration next step' $registration . data . nextStep 'LOGIN_AND_COMPLETE_QUALIFICATIONS'
2026-07-30 18:01:36 +08:00
$tenantId = [ long ] $registration . data . tenantId
$merchantLogin = Invoke-Json POST " $AuthUrl /api/auth/login" @ { username = $username ; password = $password }
2026-08-04 09:05:28 +08:00
Assert-Success 'merchant login' $merchantLogin
2026-07-30 18:01:36 +08:00
$merchantToken = $merchantLogin . data . accessToken
2026-08-04 09:05:28 +08:00
$platformLogin = Invoke-Json POST " $AuthUrl /api/auth/login" @ { username = 'platform_admin' ; password = $PlatformPassword }
Assert-Success 'platform login' $platformLogin
$platformToken = $platformLogin . data . accessToken
$partner = Invoke-Json POST " $WarehouseUrl /api/platform/v1/partners?tenantId= $tenantId " @ {
partnerCode = "E2E-PARTNER- $stamp " ; partnerName = "Phase One Partner $stamp " ; partnerType = 'SHIPPER'
types = @ ( 'SHIPPER' , 'CONSIGNEE' ); contactName = 'Test Partner' ; contactPhone = '13800138000'
contactEmail = "partner_ $stamp @example.com" ; countryCode = 'CN' ; province = 'Guangdong' ; city = 'Shenzhen'
district = 'Nanshan' ; addressLine = 'No. 1 Test Road' ; postalCode = '518000' ; status = 'ACTIVE' ; remark = 'Phase one E2E'
} $platformToken
Assert-Success 'create tenant partner' $partner
$partnerId = [ long ] $partner . data
$warehouse = Invoke-Json POST " $WarehouseUrl /api/platform/v1/warehouse-admin" @ {
tenantId = $tenantId ; warehouseCode = "E2E-WH- $stamp " ; warehouseName = "Phase One Receiving Warehouse $stamp "
warehouseType = 'DOMESTIC' ; countryCode = 'CN' ; timezone = 'Asia/Shanghai' ; contactName = 'Test Warehouse'
contactPhone = '13800138000' ; contactEmail = "warehouse_ $stamp @example.com" ; status = 'ACTIVE' ; remark = 'Phase one E2E'
address = @ { addressType = 'WAREHOUSE' ; countryCode = 'CN' ; stateProvince = 'Guangdong' ; city = 'Shenzhen' ; district = 'Nanshan' ; postalCode = '518000' ; addressLine1 = 'No. 1 Test Road' ; addressLine2 = $null ; latitude = $null ; longitude = $null }
capabilities = @ ( @ { capabilityCode = 'RECEIVING' ; capabilityName = 'Receiving' ; enabled = $true ; cutoffTime = '16:00:00' ; description = 'Phase one E2E receiving' })
} $platformToken
Assert-Success 'create tenant receiving warehouse' $warehouse
$warehouseId = [ long ] $warehouse . data
2026-08-04 17:18:07 +08:00
if ( $PublicReceivingWarehouseId -gt 0 ) { $warehouseId = $PublicReceivingWarehouseId }
2026-08-04 09:05:28 +08:00
2026-07-30 18:01:36 +08:00
$order = @ {
2026-08-04 09:05:28 +08:00
customerId = $partnerId ; customerName = "Phase One Partner $stamp " ; transportMode = 'SEA' ; tradeTerm = 'FOB'
2026-08-04 17:18:07 +08:00
destinationCountryCode = 'US' ; destinationWarehouseId = $ ( if ( $PublicDestinationWarehouseId -gt 0 ) { $PublicDestinationWarehouseId } else { $null }); destinationWarehouseName = 'ONT8'
2026-07-30 18:01:36 +08:00
expectedShipDate = '2026-08-15' ; customerRemark = 'Phase one E2E' ; pickupMethod = 'PICKUP'
2026-08-04 09:05:28 +08:00
receivingWarehouseId = $warehouseId ; receivingWarehouseName = "Phase One Receiving Warehouse $stamp " ; channelCode = 'SEA-US-FBA'
2026-08-04 17:18:07 +08:00
channelName = 'US Sea Channel' ; carrierId = 9900001 ; carrierName = '模拟海运船司' ; taxMode = 'BUY_DECLARATION' ; containerMode = 'LCL' ; customsInspection = $false
2026-07-30 18:01:36 +08:00
destinationType = 'FBA' ; amazonWarehouseCode = 'ONT8' ; shipmentId = "FBA $stamp " ; referenceId = "REF $stamp "
sender = @ { companyName = "Phase One Enterprise $stamp " ; contactName = 'Test Sender' ; phone = '13800138000' ; email = "p $stamp @example.com" ; countryCode = 'CN' ; province = 'Guangdong' ; city = 'Shenzhen' ; district = 'Nanshan' ; addressLine = 'No. 1 Test Road' ; postalCode = '518000' }
receiver = @ { companyName = 'Amazon' ; contactName = 'ONT8' ; phone = '1234567890' ; email = 'ont8@example.com' ; countryCode = 'US' ; province = 'CA' ; city = 'Moreno Valley' ; district = 'Riverside' ; addressLine = '24300 Nandina Ave' ; postalCode = '92551' }
cargoes = @ ( @ { cargoName = 'Bluetooth Speaker' ; hsCode = '8518210000' ; packageType = 'CARTON' ; packageCount = 2 ; grossWeightKg = 20.5 ; volumeCbm = 0.2 ; lengthCm = 50 ; widthCm = 40 ; heightCm = 30 ; declaredValue = 200 ; currency = 'USD' ; dangerousGoods = $false ; cargoRemark = 'Phase one test' ; skuNo = "SKU $stamp " ; upcNo = '123456789012' ; cargoNameEn = 'Bluetooth Speaker' ; customsHsCode = '8518210000' ; unitsPerPackage = 10 ; totalQuantity = 20 ; netWeightKg = 18 ; unitDeclaredValue = 10 ; material = 'Plastic' ; usage = 'Audio' ; model = 'P1' ; brand = 'Demo' })
}
2026-08-04 09:05:28 +08:00
$draftKey = "phase1-draft- $stamp "
$submitKey = "phase1-submit- $stamp "
$draft = Invoke-Json POST " $OrderUrl /api/merchant/v1/orders/first-leg" $order $merchantToken @ { 'Idempotency-Key' = $draftKey }
Assert-Success 'pre-approval draft create' $draft
$orderId = [ long ] $draft . data
$preApproval = Invoke-Json POST " $OrderUrl /api/merchant/v1/orders/first-leg/ $orderId /submit" $null $merchantToken @ { 'Idempotency-Key' = $submitKey }
Assert-Equal 'pre-approval submit code' $preApproval . code 'A0403'
2026-07-30 18:01:36 +08:00
2026-08-04 09:05:28 +08:00
$creditCode = "91 $(( Get-Date ). ToString ( 'yyyyMMddHHmmss' )) A1"
$qualificationTypes = @ ( 'BUSINESS_LICENSE' , 'LEGAL_PERSON_ID' , 'FREIGHT_FORWARDER_CERTIFICATE' , 'BANK_ACCOUNT_LICENSE' )
$uploads = @ {}
foreach ( $qualificationType in $qualificationTypes ) {
$query = "qualificationType= $qualificationType &replace=false"
if ( $qualificationType -eq 'BUSINESS_LICENSE' ) { $query += "&certificateNo= $creditCode " }
$uploadJson = & curl . exe -sS -X POST " $FileUrl /api/merchant/v1/files/qualifications? $query " `
-H "Authorization: Bearer $merchantToken " -F "file=@ $ImagePath ;type=image/png"
if ( $LASTEXITCODE -ne 0 ) { throw "qualification upload transport failed: $qualificationType " }
$upload = $uploadJson | ConvertFrom-Json
Assert-Success "upload $qualificationType " $upload
Assert-Equal "upload $qualificationType tenant status" $upload . data . tenantStatus 'PROFILE_REQUIRED'
$uploads [ $qualificationType ] = $upload
}
$merchantQualifications = Invoke-Json GET " $IamUrl /api/merchant/v1/iam/tenant/qualifications" $null $merchantToken
Assert-Success 'merchant qualification list' $merchantQualifications
$pendingTypes = @ ( $merchantQualifications . data | Where-Object { $_ . status -eq 'PENDING' } | ForEach-Object { $_ . qualificationType } | Sort-Object -Unique )
Assert-Equal 'pending qualification count' $pendingTypes . Count 4
foreach ( $qualificationType in $qualificationTypes ) {
if ( $qualificationType -notin $pendingTypes ) { throw "missing pending qualification: $qualificationType " }
}
$submitted = Invoke-Json POST " $IamUrl /api/merchant/v1/iam/tenant/qualifications/submit-review" $null $merchantToken
Assert-Success 'submit enterprise review' $submitted
Assert-Equal 'submitted tenant status' $submitted . data . status 'PENDING'
2026-07-30 18:01:36 +08:00
$reviews = Invoke-Json GET " $IamUrl /api/platform/v1/iam/reviews" $null $platformToken
2026-08-04 09:05:28 +08:00
Assert-Success 'platform review list' $reviews
2026-07-30 18:01:36 +08:00
$review = @ ( $reviews . data ) | Where-Object { [ long ] $_ . tenantId -eq $tenantId } | Select-Object -First 1
if ( $null -eq $review ) { throw 'pending review not found' }
$qualifications = Invoke-Json GET " $IamUrl /api/platform/v1/iam/tenants/ $tenantId /qualifications" $null $platformToken
2026-08-04 09:05:28 +08:00
Assert-Success 'platform qualification list' $qualifications
Assert-Equal 'qualification count' @ ( $qualifications . data ). Count 4
$fileName = Split-Path $uploads [ 'BUSINESS_LICENSE' ]. data . documentUrl -Leaf
2026-07-30 18:01:36 +08:00
$preview = Invoke-WebRequest -UseBasicParsing -Uri " $FileUrl /api/platform/v1/files/qualifications/ $tenantId / $fileName " `
-Headers @ { Authorization = "Bearer $platformToken " } -TimeoutSec 20
Assert-Equal 'platform preview status' $preview . StatusCode 200
2026-08-04 09:05:28 +08:00
$approval = Invoke-Json POST " $IamUrl /api/platform/v1/iam/reviews/ $( $review . id ) /approve" @ { comment = 'Phase one E2E approved' } $platformToken
Assert-Success 'approve enterprise review' $approval
Assert-Equal 'registered owner retained' $approval . data . administratorCreated $false
2026-07-30 18:01:36 +08:00
2026-08-04 09:05:28 +08:00
$approvedQualifications = Invoke-Json GET " $IamUrl /api/merchant/v1/iam/tenant/qualifications" $null $merchantToken
Assert-Success 'approved qualification list' $approvedQualifications
$approvedTypes = @ ( $approvedQualifications . data | Where-Object { $_ . status -eq 'APPROVED' } | ForEach-Object { $_ . qualificationType } | Sort-Object -Unique )
Assert-Equal 'approved qualification count' $approvedTypes . Count 4
$merchantProfile = Invoke-Json GET " $IamUrl /api/merchant/v1/iam/tenant/profile" $null $merchantToken
Assert-Success 'merchant profile after approval' $merchantProfile
Assert-Equal 'active tenant status' $merchantProfile . data . status 'ACTIVE'
$approvedSubmit = Invoke-Json POST " $OrderUrl /api/merchant/v1/orders/first-leg/ $orderId /submit" $null $merchantToken @ { 'Idempotency-Key' = $submitKey }
Assert-Success 'approved order submit' $approvedSubmit
2026-07-30 18:01:36 +08:00
$detail = Invoke-Json GET " $OrderUrl /api/merchant/v1/orders/first-leg/ $orderId " $null $merchantToken
Assert-Equal 'submitted order status' $detail . data . order . status 'SUBMITTED'
2026-08-04 09:05:28 +08:00
[ pscustomobject ] @ { username = $username ; tenantId = $tenantId ; registerStatus = 'PROFILE_REQUIRED' ; partnerId = $partnerId ; warehouseId = $warehouseId ; draftCreatedBeforeApproval = $true ; preApprovalSubmitCode = $preApproval . code ; pendingQualificationCount = 4 ; reviewId = $review . id ; previewHttp = 200 ; approvedQualificationCount = 4 ; orderId = $orderId ; finalOrderStatus = 'SUBMITTED' } | ConvertTo-Json