shopify学习路线
shopify学习路线
https://shopify.dev/concepts/apps 了解shopifyApp 以及三种app的区别
https://shopify.dev/tutorials/authenticate-with-oauth shopifyapp安装和认证 并获取access_token
创建一个shopifyApp https://www.shopify.com/partners 点击login登录shopify开发账户
可以查看shopifyApp列表并且创建shopifyapp 开发新项目建议使用public app
public app在正式<u>发布之前</u>是无法被<u>非测试商店</u>安装的
如果正式商店想在发布之前安装app,需要使用customer app
image.pngshopify授权和安装代码可以参考omnipersonalitypublic项目中shopify包下的com.uniprocessing.omnipersonalitypublic.shopify.controller.installcontroller
package com.uniprocessing.omnipersonalitypublic.shopify.controller;
import com.alibaba.fastjson.JSON;
import com.uniprocessing.omnipersonalitypublic.domain.dataobject.ShopDo;
import com.uniprocessing.omnipersonalitypublic.domain.dataobject.ShopSettingDo;
import com.uniprocessing.omnipersonalitypublic.service.ShopService;
import com.uniprocessing.omnipersonalitypublic.service.ShopSettingService;
import com.uniprocessing.omnipersonalitypublic.shopify.service.ImportDataService;
import com.uniprocessing.omnipersonalitypublic.util.HMACSHA256;
import com.uniprocessing.omnipersonalitypublic.util.HttpClientUntil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
/**
* @author DELL
*/
@Controller
@RequestMapping("install")
public class InstallController {
@Autowired
private ImportDataService importDataService;
@Value("${serverUrl}")
private String serverUrl;
@Value("${pageUrl}")
private String pageUrl;
@Resource
private ShopService shopService;
@Resource
private ShopSettingService shopSettingService;
@Value("${APIKey}")
private String apiKey;
@Value("${APISecretkey}")
private String apiSecretkey;
//ToDO 正式上线打开
// @Value("${server.servlet.context-path}")
// private String context;
private String context = "/omniPersonalityUM/";
@Autowired
private HttpServletRequest request;
private Logger logger = LoggerFactory.getLogger(InstallController.class);
@RequestMapping(value = "install", method = RequestMethod.GET)
public void install(HttpServletResponse response) throws IOException {
Map<String, String[]> hashMap = request.getParameterMap();
Set<String> keySet = hashMap.keySet();
StringBuilder stringBuilder = new StringBuilder();
for (String key : keySet) {
if (("hmac").equals(key)) {
continue;
}
String value = hashMap.get(key)[0];
if (value != null) {
stringBuilder.append(key);
stringBuilder.append("=");
stringBuilder.append(value);
stringBuilder.append("&");
}
}
stringBuilder.deleteCharAt(stringBuilder.lastIndexOf("&"));
String message = stringBuilder.toString();
String shop = hashMap.get("shop")[0];
String hmac1 = HMACSHA256.sha256_HMAC(message, apiSecretkey);
String hmac = hashMap.get("hmac")[0];
if (!hmac1.equals(hmac)) {
return;
}
ShopDo shopDo = shopService.getByMyShopIfyDomain(shop);
//已安装
if (shopDo != null && !shopDo.isUninstalled()) {
Cookie cookieShop = new Cookie("shopId", shopDo.getId() + "");
cookieShop.setPath(context);
Cookie timezone = new Cookie("timezone", shopDo.getTimezone().substring(1, 10));
timezone.setPath(context);
response.addCookie(cookieShop);
response.addCookie(timezone);
response.sendRedirect(pageUrl + "/distHome/index.html");
}
//未安装
else if (shopDo == null) {
//获取accessToken
String accessToken = getAccessToken(shop, hashMap);
//导入数据
shopDo = importDataService.saveShop(shop, accessToken);
Long shopId = shopDo.getId();
importDataService.saveProducts(shop, shopId, accessToken);
importDataService.createWebHooks(shop, accessToken);
//全局默认设置
ShopSettingDo shopSettingDo = new ShopSettingDo();
shopSettingDo.setShopId(shopId);
shopSettingService.save(shopSettingDo);
logger.info(shop + "新店铺安装");
//放入cookie
Cookie cookieShop = new Cookie("shopId", shopId + "");
cookieShop.setPath(context);
Cookie timezone = new Cookie("timezone", shopDo.getTimezone().substring(1, 10));
timezone.setPath(context);
response.addCookie(cookieShop);
response.addCookie(timezone);
response.sendRedirect(pageUrl + "/distHome/index.html");
}
//再次安装
else {
String accessToken = getAccessToken(shop, hashMap);
Long shopId = shopDo.getId();
//更新数据
importDataService.updateShop(shop, accessToken);
importDataService.saveProducts(shop, shopId, accessToken);
importDataService.clearWebHook(shop, accessToken);
importDataService.createWebHooks(shop, accessToken);
logger.info(shop + "再次安装");
Cookie cookieShop = new Cookie("shopId", shopId + "");
cookieShop.setPath(context);
Cookie timezone = new Cookie("timezone", shopDo.getTimezone().substring(1, 10));
timezone.setPath(context);
response.addCookie(cookieShop);
response.addCookie(timezone);
response.sendRedirect(pageUrl + "/distHome/index.html");
}
}
/**
* 授权
*
* @param httpServletRequest
* @return
*/
@RequestMapping(value = "/authorization", method = RequestMethod.GET)
public String authorization(HttpServletRequest httpServletRequest) {
Map<String, String[]> hashMap = httpServletRequest.getParameterMap();
String shop = hashMap.get("shop")[0];
String url = "https://" + shop + "/admin/oauth/authorize?client_id=" + apiKey + "&scope=read_orders,read_products,write_script_tags,write_themes,write_online_store_pages&redirect_uri=" + serverUrl + "/install/install";
return "redirect:" + url;
}
/**
* 根据商店名称获取accessToken
*
* @param shop
* @return
*/
private String getAccessToken(String shop, Map<String, String[]> parameterMap) {
String url = "https://" + shop + "/admin/oauth/access_token";
HashMap<String, String> map = new HashMap<>(4);
map.put("client_id", apiKey);
map.put("client_secret", apiSecretkey);
map.put("code", parameterMap.get("code")[0]);
String json = JSON.toJSONString(map);
String accessTokenJson = HttpClientUntil.doPost(url, json, null);
Map<String, String> parse = (Map<String, String>) JSON.parse(accessTokenJson);
HashMap<String, String> shopMap = new HashMap<>(3);
shopMap.put("shop", shop);
shopMap.put("accessToken", parse.get("access_token"));
return parse.get("access_token");
}
}
/authorization(例如:htt ps://prisrv.uniprocessing.com/orderMergeUM/install/authorization) 授权全路径必须要配置到shopifyApp 的App Url中
scope 代表所需要的权限 write 权限包含read权限
参考文档:https://shopify.dev/tutorials/authenticate-with-oauth#step-2-ask-for-permission
重点:
https://shopify.dev/docs/admin-api admin api 提供restful和graphql 两种方式,通过admin api 可以获取商家的店铺数据(例如:商品数据,订单数据,店铺数据 ,还可修改shopify的前台页面(需要相应的权限))
https://shopify.dev/docs/admin-api/rest/reference/events/webhook 创建webhook 之后,每当有店铺更新事件(例如商品添加,订单创建)发生,shopify会向我们的服务器发送对应数据,保证数据的实时更新,不同事件webhook的创建依旧需要对应的admin权限
https://shopify.dev/docs/admin-api/rest/reference/products/smartcollection 商品集合分为两种smart和customer,只有smart集合更新和创建的时候才会触发shopify的集合webhook
https://shopify.dev/docs/themes/ajax-api ajax api 免授权,可以在前台页面(页面路径需要被shopify代理,详情见shopify扩展功能)使用
https://shopify.dev/docs/app-extensions/extension-points shopify扩展功能
https://shopify.dev/concepts/app-store/getting-your-app-approved/app-requirements#a-authentication public app 的要求
ps :代码中有TODO标记的地方,需要按照不同情况进行修改
项目部署位置:
/home/用户名称/server/项目名称
例如 /home/devapp/server/omniBizify
静态资源地址(页面之类的) /home/用户名称/nginx/nginx-omni
例如 /home/devapp/nginx/nginx-omni