defmodule DaProductApp.MerchantRegistration.AlipayProvider do @moduledoc """ Alipay-specific merchant registration and inquiry logic. """ require Logger @doc """ Alipay-specific registration logic """ def register(params) do base_url = "https://open-sea-global.alipayplus.com/aps/api/v1/merchants/registration" client_id = "SANDBOX_5YEV5L30082Z03013" private_key = """ -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCJOxzYbsgCClFHEV/n1c2uCDuMQyMpw7VedsTFWOZCr/6b5t1Dz8PJXnsMXfrK4mH97UGOBLjGomyA7AszbsZzb2PG8OJeFdIlJGrq3FuOBXfE0WIzveB+H8X0GxCaMAZOM0EJliV8Zg0tPNYTjl9DIc9Zq6gK0u8CfG3IIPugJUPxA6uoWuDYKGq/zSpPazFt+AmLNN7J4fiZNGH/BRZcOfoZhuuhn56eMAcH6x+tub8AKVJLgTIx+KqRrCcsne3IN0ddpMAA745lvcLcvbYkDKGp2qNNtER1lDU/x6ao9h6uLV4CGPgFhOUTm+7lNvRtB11duWatYZY/NjSCGHMTAgMBAAECggEAL5BgiBellSd0UliQUC+HsYlC8nOWrXQa2dn6i5grfvO3INwc1tMdPh9UMM4mDcn3QubH8OxsCtTjHLAzlakQeZQjFiIJo6iWhK8hq7OivA/jGkGkcuCd/bkPiHMVBwwcM2CKa0MyTPKmIIbUgES5efAvCRp5DP9dPhRYjKP58uBUNXNETO+aCUjCZDeT1ciqhCMg53JQ2r299EKNhUcxBBH2OOdhhC2ofQhbpIqxyRG/mSE1uZEFdAmCmNfHYDjOM9PRkRwsWgwLnkyOhtp6ine73P+DG16xl5do/ejbSJ09lw64bxgfQwCN7/RuLq0rtVqujPaBoXkx4SAXU64hCQKBgQDa1BG7gLCI0A2GarBj2QDJbv+on/mYHZYeP53HU3RZl8SPTMA7qrfJ2PwHzvV1R3WGhvdYZVGQqv4r2+8q2X5kI1HA6ejAtNpLdskiVXqWlqUGLb9tWFTgS3oWBMzEVzV/kV6NiLUmWO8HI1GfyHIFPLSVWj2fYwCKCotYmqNFFwKBgQCgirXZTAM1dNGPpjk7S6PYsvUe/mB/bbTwSvQ6LXe2524Tuo6OvsdSGN+789Yd8Wxp2VqPtwYLS94H5Y2PBCEN6ftifXO9UdOJwDGq9bhiUSP16F+vxzZsyGm0a5ErIUviOPfjZhP3apE8UeKT3rZS7zNOFihLVMLEQP9KFUP3ZQKBgHy97UnUp02mRD9+rASPHHq3cre+Ufrbysp9e0S4FxhHgr4pg1/ABrrinXEaEiSD0sQYRgG26BMu1mtMGX90si8FT0JIVO0da18fXLLcxV/4iiQGihwcAW5GuFa677tw90c8KAlIh/NPORr5kDskeZLwswR8h6pHNnR6ZErjA/WLAoGAVAtp2eEuSNzoHGCz03PsybQeGOSole1T7PwAUTieVHVhrhhbKyV66WK2NgoXzMMns14jR9tT4bQM/2tQKU/LEiKtBMmSPslIifPAzLQom+fIgKLu/PG4b0iX9ejeLYsX081pEHXO/BahA8gGas0L++zXmgiFfbJY6C7yttDdLPUCgYEAxfr4KLo+n9QvSopKV04KrVyxHIgKPY9gdBWCE9RqmpYIt2ou29fY1/dh3Vt4EU5TDAN1LiX/zmoAfPAmuQJwYmWbnILczIvCGx0PUGMUn2qCo62qKMuFzdWjvyLT3HW7pbiiIJbD3PvTDnRM0O4+gS+mBaktmv7upz3qlPYh554= -----END PRIVATE KEY----- """ # Transform generic format to Alipay format alipay_params = transform_to_alipay_format(params) timestamp = DateTime.utc_now() |> DateTime.to_iso8601() path = "/aps/api/v1/merchants/registration" body = Jason.encode!(alipay_params) payload = construct_alipay_payload("POST", path, client_id, timestamp, alipay_params) signature = generate_alipay_signature(payload, private_key) headers = [ {"Content-Type", "application/json; charset=UTF-8"}, {"signature", "algorithm=RSA256, keyVersion=0, signature=#{signature}"}, {"client-id", client_id}, {"request-time", timestamp} ] Logger.debug("Sending request to Alipay with headers: #{inspect(headers)} and body: #{body}") case HTTPoison.post(base_url, body, headers) do {:ok, %HTTPoison.Response{status_code: 200, body: response_body}} -> Logger.info("Raw response from Alipay: #{response_body}") {:ok, Jason.decode!(response_body)} {:ok, %HTTPoison.Response{status_code: status_code, body: response_body}} -> Logger.warning("Non-200 response from Alipay: #{response_body}", []) {:error, %{status_code: status_code, response: response_body}} {:error, %HTTPoison.Error{reason: reason}} -> Logger.error("HTTPoison error: #{inspect(reason)}") {:error, "Network error: #{inspect(reason)}"} end end @doc """ Alipay-specific inquiry logic """ def inquire(params) do base_url = "https://open-sea-global.alipayplus.com/aps/api/v1/merchants/inquiryRegistrationStatus" client_id = "SANDBOX_5YEV5L30082Z03013" private_key = """ -----BEGIN PRIVATE KEY----- MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCJOxzYbsgCClFHEV/n1c2uCDuMQyMpw7VedsTFWOZCr/6b5t1Dz8PJXnsMXfrK4mH97UGOBLjGomyA7AszbsZzb2PG8OJeFdIlJGrq3FuOBXfE0WIzveB+H8X0GxCaMAZOM0EJliV8Zg0tPNYTjl9DIc9Zq6gK0u8CfG3IIPugJUPxA6uoWuDYKGq/zSpPazFt+AmLNN7J4fiZNGH/BRZcOfoZhuuhn56eMAcH6x+tub8AKVJLgTIx+KqRrCcsne3IN0ddpMAA745lvcLcvbYkDKGp2qNNtER1lDU/x6ao9h6uLV4CGPgFhOUTm+7lNvRtB11duWatYZY/NjSCGHMTAgMBAAECggEAL5BgiBellSd0UliQUC+HsYlC8nOWrXQa2dn6i5grfvO3INwc1tMdPh9UMM4mDcn3QubH8OxsCtTjHLAzlakQeZQjFiIJo6iWhK8hq7OivA/jGkGkcuCd/bkPiHMVBwwcM2CKa0MyTPKmIIbUgES5efAvCRp5DP9dPhRYjKP58uBUNXNETO+aCUjCZDeT1ciqhCMg53JQ2r299EKNhUcxBBH2OOdhhC2ofQhbpIqxyRG/mSE1uZEFdAmCmNfHYDjOM9PRkRwsWgwLnkyOhtp6ine73P+DG16xl5do/ejbSJ09lw64bxgfQwCN7/RuLq0rtVqujPaBoXkx4SAXU64hCQKBgQDa1BG7gLCI0A2GarBj2QDJbv+on/mYHZYeP53HU3RZl8SPTMA7qrfJ2PwHzvV1R3WGhvdYZVGQqv4r2+8q2X5kI1HA6ejAtNpLdskiVXqWlqUGLb9tWFTgS3oWBMzEVzV/kV6NiLUmWO8HI1GfyHIFPLSVWj2fYwCKCotYmqNFFwKBgQCgirXZTAM1dNGPpjk7S6PYsvUe/mB/bbTwSvQ6LXe2524Tuo6OvsdSGN+789Yd8Wxp2VqPtwYLS94H5Y2PBCEN6ftifXO9UdOJwDGq9bhiUSP16F+vxzZsyGm0a5ErIUviOPfjZhP3apE8UeKT3rZS7zNOFihLVMLEQP9KFUP3ZQKBgHy97UnUp02mRD9+rASPHHq3cre+Ufrbysp9e0S4FxhHgr4pg1/ABrrinXEaEiSD0sQYRgG26BMu1mtMGX90si8FT0JIVO0da18fXLLcxV/4iiQGihwcAW5GuFa677tw90c8KAlIh/NPORr5kDskeZLwswR8h6pHNnR6ZErjA/WLAoGAVAtp2eEuSNzoHGCz03PsybQeGOSole1T7PwAUTieVHVhrhhbKyV66WK2NgoXzMMns14jR9tT4bQM/2tQKU/LEiKtBMmSPslIifPAzLQom+fIgKLu/PG4b0iX9ejeLYsX081pEHXO/BahA8gGas0L++zXmgiFfbJY6C7yttDdLPUCgYEAxfr4KLo+n9QvSopKV04KrVyxHIgKPY9gdBWCE9RqmpYIt2ou29fY1/dh3Vt4EU5TDAN1LiX/zmoAfPAmuQJwYmWbnILczIvCGx0PUGMUn2qCo62qKMuFzdWjvyLT3HW7pbiiIJbD3PvTDnRM0O4+gS+mBaktmv7upz3qlPYh554= -----END PRIVATE KEY----- """ # Transform generic merchantId to Alipay's referenceMerchantId inquiry_params = %{"referenceMerchantId" => params["merchantId"]} timestamp = DateTime.utc_now() |> DateTime.to_iso8601() path = "/aps/api/v1/merchants/inquiryRegistrationStatus" body = Jason.encode!(inquiry_params) payload = construct_alipay_payload("POST", path, client_id, timestamp, inquiry_params) signature = generate_alipay_signature(payload, private_key) headers = [ {"Content-Type", "application/json; charset=UTF-8"}, {"signature", "algorithm=RSA256, keyVersion=0, signature=#{signature}"}, {"client-id", client_id}, {"request-time", timestamp} ] Logger.debug("Sending inquiry to Alipay with headers: #{inspect(headers)} and body: #{body}") case HTTPoison.post(base_url, body, headers) do {:ok, %HTTPoison.Response{status_code: 200, body: response_body}} -> Logger.info("Inquiry response from Alipay: #{response_body}") {:ok, Jason.decode!(response_body)} {:ok, %HTTPoison.Response{status_code: status_code, body: response_body}} -> Logger.warning("Non-200 inquiry response from Alipay: #{response_body}", []) {:error, %{status_code: status_code, response: response_body}} {:error, %HTTPoison.Error{reason: reason}} -> Logger.error("HTTPoison error during inquiry: #{inspect(reason)}") {:error, "Network error: #{inspect(reason)}"} end end # Transform generic format to Alipay-specific format defp transform_to_alipay_format(params) do merchant = params["merchant"] || %{} store = params["store"] || %{} # Transform merchant info merchant_info = %{ "referenceMerchantId" => merchant["id"], "merchantDisplayName" => merchant["displayName"], "merchantMCC" => merchant["mcc"], "websites" => transform_websites_for_alipay(merchant["websites"]), "merchantAddress" => transform_address_for_alipay(merchant["address"]), "registrationDetail" => transform_legal_info_for_alipay(merchant["legalInfo"]), "shareholderName" => get_in(merchant, ["ownerInfo", "name"]), "shareholderId" => get_in(merchant, ["ownerInfo", "id"]) } # Transform store info store_info = %{ "referenceStoreId" => store["id"], "storeName" => store["name"], "storeMCC" => store["mcc"], "storeAddress" => transform_address_for_alipay(store["address"]) } %{ "registrationRequestId" => params["requestId"], "merchantInfo" => merchant_info, "storeInfo" => store_info, "productCodes" => transform_services_for_alipay(params["services"]) } end # Transform websites array from generic to Alipay format defp transform_websites_for_alipay(nil), do: nil defp transform_websites_for_alipay(websites) when is_list(websites) do Enum.map(websites, fn website -> %{ "url" => website["url"], "websiteType" => website["type"] || "WEB" } end) end defp transform_websites_for_alipay(_), do: nil # Transform address from generic to Alipay format defp transform_address_for_alipay(nil), do: nil defp transform_address_for_alipay(address) do %{ "region" => address["country"] || address["state"], "address1" => address["street"] } end # Transform legal info from generic to Alipay registration detail format defp transform_legal_info_for_alipay(nil), do: nil defp transform_legal_info_for_alipay(legal_info) do %{ "legalName" => legal_info["legalName"], "registrationType" => map_registration_type_for_alipay(legal_info["registrationType"]), "registrationNo" => legal_info["registrationNumber"], "registrationAddress" => transform_address_for_alipay(legal_info["registrationAddress"]), "businessType" => map_business_type_for_alipay(legal_info["businessType"]) } end # Map generic registration types to Alipay's expected enum values defp map_registration_type_for_alipay(nil), do: "ENTERPRISE_REGISTRATION_NO" defp map_registration_type_for_alipay(type) do case String.upcase(type) do "LLC" -> "ENTERPRISE_REGISTRATION_NO" "CORP" -> "ENTERPRISE_REGISTRATION_NO" "CORPORATION" -> "ENTERPRISE_REGISTRATION_NO" "PARTNERSHIP" -> "ENTERPRISE_REGISTRATION_NO" "SOLE_PROPRIETORSHIP" -> "INDIVIDUAL_REGISTRATION_NO" "INDIVIDUAL" -> "INDIVIDUAL_REGISTRATION_NO" _ -> "ENTERPRISE_REGISTRATION_NO" # Default fallback end end # Map generic business types to Alipay's expected enum values defp map_business_type_for_alipay(nil), do: "ENTERPRISE" defp map_business_type_for_alipay(type) do case String.upcase(type) do "RETAIL" -> "ENTERPRISE" "SERVICE" -> "ENTERPRISE" "RESTAURANT" -> "ENTERPRISE" "ECOMMERCE" -> "ENTERPRISE" "INDIVIDUAL" -> "INDIVIDUAL" "PERSONAL" -> "INDIVIDUAL" _ -> "ENTERPRISE" # Default fallback end end # Transform generic services to Alipay product codes defp transform_services_for_alipay(nil), do: [] defp transform_services_for_alipay(services) when is_list(services) do # Map generic service names to Alipay product codes Enum.map(services, fn service -> case service do "PAYMENT_PROCESSING" -> "AGREEMENT_PAYMENT" "ONLINE_PAYMENTS" -> "IN_STORE_PAYMENT" "QR_PAYMENTS" -> "QR_CODE_PAYMENT" _ -> service # Pass through unknown services as-is end end) end defp transform_services_for_alipay(_), do: [] # Helper functions for Alipay API calls defp construct_alipay_payload(_method, path, client_id, timestamp, request_body) do body = Jason.encode!(request_body) "POST #{path}\n#{client_id}.#{timestamp}.#{body}" end defp generate_alipay_signature(payload, private_key) do Logger.debug("Payload to sign: #{payload}") case :public_key.pem_decode(private_key) do [entry] -> rsa_private_key = :public_key.pem_entry_decode(entry) signature = :public_key.sign(payload, :sha256, rsa_private_key) Base.url_encode64(signature, padding: false) [] -> Logger.error("Failed to decode private key") raise ArgumentError, "Invalid PEM format for private key" end end end