shopify 应用对接电商示例

前言

shopify 平台感兴趣的自己搜一下了
我这儿的业务场景是:
我方作为应用提供方,让商户接入、安装我方应用,我方通过商户返回的code拿到商户授权的 accessToken 进行接口访问

这里对应用方和商户的首次交互做个案例
shopify OAuth accessToken 生成介绍

OAuth首次交互流程

下图在官方的API文档中可以找到,这里贴了出来
在这里插入图片描述

scope列表

权限名称描述
read_analyticsView store metrics
read_assigned_fulfillment_orders, write_assigned_fulfillment_ordersView or manage fulfillment orders
read_customers, write_customersView or manage customers, customer addresses, order history, and customer groups
read_discounts, write_discountsView or manage automatic discounts and discount codes
read_draft_orders, write_draft_ordersView or manage orders created by merchants on behalf of customers
read_files, write_filesView or manage files
read_fulfillments, write_fulfillmentsView or manage fulfillment services
read_gdpr_data_requestView GDPR data requests
read_gift_cards, write_gift_cardsView or manage gift cards (Available to Plus merchants only)
read_inventory, write_inventoryView or manage inventory across multiple locations
read_legal_policies, write_legal_policiesView or manage a shop’s legal policies
read_locationsView the geographic location of stores, headquarters, and warehouses
read_marketing_events, write_marketing_eventsView or manage marketing events and engagement data
read_merchant_managed_fulfillment_orders, write_merchant_managed_fulfillment_ordersView or manage fulfilment orders assigned to merchant-managed locations
read_online_store_navigationView menus for display on the storefront
read_online_store_pages, write_online_store_pagesView or manage Online Store pages
read_order_edits, write_order_editsView or manage edits to orders
read_orders, write_orders, read_all_ordersView or manage orders, transactions, fulfillments, and abandoned checkouts from the last 60 days, or View all past and future orders
read_price_rules, write_price_rulesView or manage conditional discounts
read_products, write_productsView or manage products, variants, and collections
read_product_listings, write_product_listingsView or manage product or collection listings
read_reports, write_reportsView or manage reports on the Reports page in the Shopify admin
read_resource_feedbacks, write_resource_feedbacksView or manage the status of shops and resources
read_script_tags, write_script_tagsView or manage the JavaScript code in storefront or orders status pages
read_shipping, write_shippingView or manage shipping carriers, countries, and provinces
read_shopify_payments_accountsView Shopify Payments accounts
read_shopify_payments_bank_accountsView bank accounts that can receive Shopify Payment payouts
read_shopify_payments_disputesView Shopify Payment disputes raised by buyers
read_shopify_payments_payoutsView Shopify Payments payouts and the account’s current balance
read_content, write_contentView or manage articles, blogs, comments, pages, and redirects
read_themes, write_themesView or manage theme templates and assets
read_third_party_fulfillment_orders, write_third_party_fulfillment_ordersView or manage fulfillment orders assigned to a location managed by any fulfillment service
read_translations, write_translationsView or manage content that can be translated

java代码示例

接口


import cn.hutool.crypto.digest.HMac;
import cn.hutool.crypto.digest.HmacAlgorithm;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.servlet.ModelAndView;

import javax.annotation.Resource;
import java.nio.charset.StandardCharsets;
import java.util.Properties;

/**
 * shopify 接口
 * 参考文档:https://www.shopify.com/partners/blog/17056443-how-to-generate-a-shopify-api-token
 * @author Heng.Wei
 * @date 2022-10-11
 **/
@Slf4j
@Controller
@RequestMapping("/shopify")
public class ShopifyController {

    /** API key of your app. */
    private final static String CLIENT_KEY = "[注册的应用的API KEY]";
    /** 加密的密钥 */
    private final static String CLIENT_SECRET = "[API SECRET]";
    /** 摘要算法 */
    private final static HMac HMAC = new HMac(HmacAlgorithm.HmacSHA256, CLIENT_SECRET.getBytes(StandardCharsets.UTF_8));
    /** 向商户申请授权码 */
    private final static String AUTHORIZE_URL = "https://%s/admin/oauth/authorize?client_id=%s&grant_options[]=%s&redirect_uri=%s&scope=%s";
    /** 生成 accessToken 地址, 带三个参数 client_id、client_secret、code */
    private final static String ACCESS_TOKEN_URL = "https://%s/admin/oauth/access_token";
    /** 权限范围 */
    private final static String SCOPE = "write_product_listings,read_orders,write_products";
    /** 商户安装完成后的重定向地址 */
    private final static String REDIRECT_URI = "http://localhost:20008/shopify/generate_token";

    private final static String GRANT_OPTIONS = "per-user";

    @Resource
    private RestTemplate restTemplate;

    /**
     * <pre>
     * 商户发起安装请求
     * </pre>
     *
     * @param hmac      GET参数生成的摘要
     * @param host      这个参数官网没有描述
     * @param shop      商店域名
     * @param timestamp 时间戳
     * @author weiheng
     **/
    @GetMapping("/install")
    public ModelAndView index(@RequestParam("hmac") String hmac,
                              @RequestParam("host") String host,
                              @RequestParam("shop") String shop,
                              @RequestParam("timestamp") String timestamp) {

        log.info("shop[{}], hmac[{}], host[{}], timestamp[{}]", shop, hmac, host, timestamp);
        String params = "host=" + host + "&shop=" + shop + "&timestamp=" + timestamp;
        boolean verify = verify(params, hmac, shop);
        if (verify) {
            // 校验通过
            String path = String.format(AUTHORIZE_URL, shop, CLIENT_KEY, GRANT_OPTIONS, REDIRECT_URI, SCOPE);
            return new ModelAndView("redirect:" + path);
        }
        return new ModelAndView("redirect:404");
    }

    /**
     * <pre>
     * 通过商户给的 code,生成 token 凭据
     * </pre>
     *
     * @param hmac      HMAC SHA256 数字签名
     * @param code      商户给的授权码
     * @param host      官网未给出该字段定义
     * @param shop      商店域名
     * @param timestamp 时间戳
     * @author weiheng
     **/
    @GetMapping("/generate_token")
    public void generateToken(@RequestParam("hmac") String hmac,
                              @RequestParam("code") String code,
                              @RequestParam("host") String host,
                              @RequestParam("shop") String shop,
                              @RequestParam("timestamp") String timestamp) {

        log.info("shop[{}], hmac[{}], code[{}], host[{}], timestamp[{}]", shop, hmac, code, host, timestamp);
        String params = "code=" + code + "&host=" + host + "&shop=" + shop + "&timestamp=" + timestamp;
        boolean verify = verify(params, hmac, shop);

        if (!verify) {
            log.error("shop[{}], 校验未通过", shop);
            return;
        }

        // 校验通过 - Exchange access code for the shop token
        Properties request = new Properties();
        request.put("client_id", CLIENT_KEY);
        request.put("client_secret", CLIENT_SECRET);
        request.put("code", code);
        String path = String.format(ACCESS_TOKEN_URL, shop);
        JSONObject obj = restTemplate.postForObject(path, request, JSONObject.class);
        log.info("resultObj[{}]", obj);
        if (obj == null) {
            log.error("获取token异常, Properties[{}]", request);
            return;
        }
        ShopifyTokenDTO shopifyTokenDto = JSON.parseObject(obj.toJSONString(), ShopifyTokenDTO.class);
        log.info("shopifyTokenDto[{}]", shopifyTokenDto);
        // TODO 缓存 商户 的 token 凭据
    }

    /**
     * <pre>
     * 请求有效性校验
     * </pre>
     *
     * @param params ASCII排序后的参数 URL格式的参数,如  a=111&b=222&c=333
     * @param hmac   调用方传过来的摘要信息
     * @param shop   商户域名
     * @return 校验是否通过
     * @author weiheng
     **/
    private boolean verify(String params, String hmac, String shop) {
        String digest = HMAC.digestHex(params.getBytes(StandardCharsets.UTF_8));
        boolean verify = HMAC.verify(digest.getBytes(StandardCharsets.UTF_8), hmac.getBytes(StandardCharsets.UTF_8));
        log.info("shop[{}], digest[{}], verify[{}]", shop, digest, verify);
        return verify;
    }

}

自定义 token 实体


import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.io.Serializable;

/**
 * <pre>
 * shopify 商户返回的token信息
 * </pre>
 * @author Heng.Wei
 * @date 2022-10-11
 **/
@Data
public class ShopifyTokenDTO implements Serializable {

    @JSONField(name = "access_token")
    private String accessToken;

    private String scope;

    @JSONField(name = "expires_in")
    private Integer expiresIn;

    @JSONField(name = "associated_user_scope")
    private String associatedUserScope;

    private String session;

    @JSONField(name = "account_number")
    private String accountNumber;

    @JSONField(name = "associated_user")
    private ShopifyUserDTO associatedUser;
}

自定义实体类 ShopifyUserDTO


import com.alibaba.fastjson.annotation.JSONField;
import lombok.Data;

import java.io.Serializable;

/**
 * <pre>
 * shopify associated user
 * </pre>
 * @author Heng.Wei
 * @date 2022-10-11
 **/
@Data
public class ShopifyUserDTO implements Serializable {

    private Long id;

    @JSONField(name = "first_name")
    private String firstName;

    @JSONField(name = "last_name")
    private String lastName;

    private String email;

    @JSONField(name = "account_owner")
    private Boolean accountOwner;

    private String locale;

    private Boolean collaborator;

    @JSONField(name = "email_verified")
    private Boolean emailVerified;
}

亲测OK,上图

这里拿到 token 就算完事儿了
在这里插入图片描述

  • 0
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值