curl testnet order, Signature for this request is not valid error came

I tried to make an order in testnet with Rest Api using curl, but got the ‘Signature for this request is not valid’ error.
I wonder why and how to solve the problem…

I used this shell script as order.sh

#!/usr/bin/env bash

# Set up authentication:
API_KEY="Z7PcFXtO77q5n2rSSKEouEX54Gfon9Aiq346gQUGRH27RWFKPfFTPQ6qH6bNyWR2"
SECRET="......" # i hided my secret

# Set up the request:
BASE_URL="https://testnet.binance.vision"
API_METHOD="POST"
API_CALL="api/v3/order"
API_PARAMS="symbol=BTCUSDT&side=SELL&type=LIMIT&timeInForce=GTC&quantity=1&price=1"

# Sign the request:
timestamp=$(date +%s000)
api_params_with_timestamp="$API_PARAMS&timestamp=$timestamp"
signature=$(echo -n api_params_with_timestamp | \
            openssl dgst -sha256 -hmac $SECRET)

# Send the request:
curl -H 'Content-Type: application/json' -H "X-MBX-APIKEY: $API_KEY" -X "$API_METHOD" \
    "$BASE_URL/$API_CALL?$api_params_with_timestamp&signature=$signature" -v

and got this response

*   Trying 13.225.131.39:443...
* Connected to testnet.binance.vision (13.225.131.39) port 443
* ALPN: curl offers h2,http/1.1
* (304) (OUT), TLS handshake, Client hello (1):
*  CAfile: /etc/ssl/cert.pem
*  CApath: none
* (304) (IN), TLS handshake, Server hello (2):
* (304) (IN), TLS handshake, Unknown (8):
* (304) (IN), TLS handshake, Certificate (11):
* (304) (IN), TLS handshake, CERT verify (15):
* (304) (IN), TLS handshake, Finished (20):
* (304) (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / AEAD-AES128-GCM-SHA256
* ALPN: server accepted h2
* Server certificate:
*  subject: CN=*.binance.vision
*  start date: Nov 19 00:00:00 2023 GMT
*  expire date: Dec 17 23:59:59 2024 GMT
*  subjectAltName: host "testnet.binance.vision" matched cert's "*.binance.vision"
*  issuer: C=US; O=Amazon; CN=Amazon RSA 2048 M02
*  SSL certificate verify ok.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://testnet.binance.vision/api/v3/order?symbol=BTCUSDT&side=SELL&type=LIMIT&timeInForce=GTC&quantity=1&price=1&timestamp=1713065000000&signature=e2049e5eca40b1e6d9de92374aa1c7b4bc2a8759e1ad984910d30895d92ddd3d
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: testnet.binance.vision]
* [HTTP/2] [1] [:path: /api/v3/order?symbol=BTCUSDT&side=SELL&type=LIMIT&timeInForce=GTC&quantity=1&price=1&timestamp=1713065000000&signature=e2049e5eca40b1e6d9de92374aa1c7b4bc2a8759e1ad984910d30895d92ddd3d]
* [HTTP/2] [1] [user-agent: curl/8.4.0]
* [HTTP/2] [1] [accept: */*]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [x-mbx-apikey: Z7PcFXtO77q5n2rSSKEouEX54Gfon9Aiq346gQUGRH27RWFKPfFTPQ6qH6bNyWR2]
> POST /api/v3/order?symbol=BTCUSDT&side=SELL&type=LIMIT&timeInForce=GTC&quantity=1&price=1&timestamp=1713065000000&signature=e2049e5eca40b1e6d9de92374aa1c7b4bc2a8759e1ad984910d30895d92ddd3d HTTP/2
> Host: testnet.binance.vision
> User-Agent: curl/8.4.0
> Accept: */*
> Content-Type: application/json
> X-MBX-APIKEY: Z7PcFXtO77q5n2rSSKEouEX54Gfon9Aiq346gQUGRH27RWFKPfFTPQ6qH6bNyWR2
> 
< HTTP/2 400 
< content-type: application/json;charset=UTF-8
< content-length: 63
< date: Sun, 14 Apr 2024 03:23:20 GMT
< server: nginx
< x-mbx-uuid: 85a544af-7f8b-4395-99b4-d54439c89f7f
< x-mbx-used-weight: 3
< x-mbx-used-weight-1m: 3
< strict-transport-security: max-age=31536000; includeSubdomains
< x-frame-options: SAMEORIGIN
< x-xss-protection: 1; mode=block
< x-content-type-options: nosniff
< content-security-policy: default-src 'self'
< x-content-security-policy: default-src 'self'
< x-webkit-csp: default-src 'self'
< cache-control: no-cache, no-store, must-revalidate
< pragma: no-cache
< expires: 0
< x-cache: Error from cloudfront
< via: 1.1 b94a615d55ccab1b43dc5ac7105888c8.cloudfront.net (CloudFront)
< x-amz-cf-pop: ICN54-C2
< x-amz-cf-id: YNfYQIJsVsg1oWwJBfIQACAc8O-0DI07wfIRR3aVVS9aeFc7Ofxo-g==
< 
* Connection #0 to host testnet.binance.vision left intact
{"code":-1022,"msg":"Signature for this request is not valid."}%  

I think that simle appending the timestamp to the parameter list is wrong.
The API description states that the parameters should be concatenated by order of the parameter names before calculating the HMAC checksum.
In your case, you have both type= and timestamp=, so adding timestamp to the end of the list violates this rule.