Kay

V1

2023/05/11阅读:42主题:默认主题

Shopify App

背景

Shopify是一家加拿大的科技公司,旨在帮助企业或个人快速建立在线商店.2021年第四季度,一度成为了全球第三大电子商务平台.在国外也是构建电商独立站的首选.

优势

  • 易用性

    Shopify平台提供了丰富的主题模板,商家可以通过简单的拖放操作,轻松地创建和定制网站页面,而无需编写代码.

  • 可靠性

    Shopify平台具有强大的基础架构和安全性措施,包括24x7监控、备份和灾难恢复等功能,以确保商家的数据始终安全.

  • 扩展性

    Shopify App Store提供了海量的应用程序,覆盖了各种功能领域,例如市场营销、物流、客户支持等。商家可以根据自己的需求,选择并使用这些应用程序和插件,以满足不同的业务需求.

Shopify app

图中展现的是一款商品评论插件,如果商家想在自己的商品详情页添加一个用户评论功能,只需要Shopify应用商店点一下安装按钮,就能立马在自己的商城里应用这个功能.

插件不只是提供了一个漂亮的UI界面,而是一个终极的产品形态.在插件内,包含了前端的UI交互、后端的逻辑运算、数据库的存储、运维等等一整套服务.

对于商户而言,他不需要懂任何代码,只需要一件安装,就能快速享受到这一整套服务带来的好处.

对于开发者而言,他只需要将自身业务中一些优秀的组件抽离出来,放在Shopify应用商店上出售,就能实现技术变现.

整体的流程:

从上面的流程可以看出,App需要对两方提供服务.

  • App需要开发C端用户使用的产品功能
  • App需要开发商对产品的管理功能

以上图描述的评论插件举例,页面上展示所有评论的UI和功能都属于c端的功能.

同时商户需要一个地方去管理这些用户评论,比如某些不良评论需要屏蔽,这就是后台管理系统做产品支撑.

因此开发一款App的任务划分成两部份:

  • c端功能的开发
  • 后台管理系统的开发

C端应用

通过执行下面cli命令下载C端应用的模版代码,然后使用htmlcssjs开发应用功能.

npm run shopify app generate extension

后台管理系统

插件的后台管理系统集成在了Shopify后台,一旦插件部署上线,就可以在Shopify后台直接访问插件的后台管理系统(外观如下).

后台管理系统通过执行下面cli命令生成模版配置.

npm init @shopify/app@latest

后台管理系统的前端功能通过react框架和Polaris组件库开发,在与Shopify后台进行交互的过程中使用了GraphQL Admin API.

传统的前后端交互采用的都是REST API,后端定义各式各样的接口,前端按照接口的规范来请求接口获取数据.此时如果某个接口缺少一个字段,就需要后端程序员在代码中手动加上.

GraphQL允许客户端指定希望从服务器获取哪些数据,并以预期的格式返回.此时后端只需要定义一个接口,就能满足前端的所有查询要求,极大的简化了双方的工作量.

例如下面案例中使用GraphQL语法查询产品表中前3条产品数据,并且产品数据只需要返回idtitle属性.

const queryString = `{
  products (first: 3) {
    edges {
      node {
        id
        title
      }
    }
  }
}`;

const client = new shopify.clients.Graphql({session});
const products = await client.query({
  data: queryString,
});

一个GraphQL查询是一个被发往服务端的字符串,该字符串在服务端被解释和执行后返回JSON数据给客户端.

服务端需要解析GraphQL字符串,将其转化成AST语法树才能执行,因此它会以牺牲服务器的性能作为代价.

关联查询

{
  user(id: "1") {
    name
    orders(first: 3) {
      edges {
        cursor
        node {
          id
          amount
          status
        }
      }
      pageInfo {
        endCursor
        hasNextPage
      }
    }
  }
}

前言

Shopify Functions顾名思义它是一个函数.只要是函数,它都会包含三个基本要素.

  • 输入: 给函数传递的参数
  • 函数体: 函数的计算逻辑
  • 输出: 函数返回的结果

Shopify Functions不是普通函数,更像是一种定义在云端的函数.

它和Serverless架构中下的云函数是不太一样的,我们一般谈论Serverless架构中的事件驱动计算服务,都是在云端环境上部署一些计算服务.

编写这类服务时可以让开发者无需关心服务器、操作系统等底层架构,全部精力集中在业务层开发.它可以响应特定的触发器(例如 HTTP 请求、消息队列、文件上传等)

Shopify Functions这种函数它是不能通过手动方式去触发的,它会在User Journey的旅途中由Shopify控制触发它.

例如上图一个购物车页面,如果我们在插件中实现了一个Shopify Functions,当用户点击这个checkout按钮时,触发表单提交,Shopify服务器接受表单的请求.

此时Shopify服务器如何检测到商店安装了Shopify Functions,它就会去执行Shopify Functions.

输入就是购物车里面的商品数据,函数体开发者可以自由定义.比如实现一个梯度折扣的功能,遍历购物车内的商品数量,最后按照折扣返回商品结果.


Delivery Customization API 商家可以创建自定义送货选项.比如修改快递的描述、针对某款商品隐藏某些快递方式.
Order Discount API 创建一种类型折扣应用于购物车中整个订单商品
Product Discount API 创建一种类型折扣应用于购物车中特定的产品或者变体
Payment Customization API 允许商家通过API管理支付方式.例如针对某些国家或者用户隐藏某类支付方式

实现

  • 创建项目
npm run shopify app generate extension -- --type product_discounts --name=product-discount
  • 编写 input.graphql
//input.graphql
query Input {
  cart {
    lines {
      quantity
      merchandise {
        __typename
        ...on ProductVariant {
          id
        }
      }
    }
  }
  discountNode {
    metafield(namespace: "$app:volume-discount", key: "function-configuration") {
      value
    }
  }
}
  • 编写函数
import { DiscountApplicationStrategy } from "../generated/api";

/**
 * @type {FunctionResult}
 */
const EMPTY_DISCOUNT = {
  discountApplicationStrategy: DiscountApplicationStrategy.First,
  discounts: [],
};

export default (input) => {
    const configuration = JSON.parse(
      input?.discountNode?.metafield?.value ?? "{}"
    );
    if (!configuration.quantity || !configuration.percentage) {
      return EMPTY_DISCOUNT;
    }
    const targets = input.cart.lines
      .filter(line => line.quantity >= configuration.quantity &&
        line.merchandise.__typename == "ProductVariant")
      .map(line => {
        const variant = /** @type {ProductVariant} */ (line.merchandise);
        return /** @type {Target} */ ({
          productVariant: {
            id: variant.id
          }
        });
      });

    if (!targets.length) {
      console.error("No cart lines qualify for volume discount.");
      return EMPTY_DISCOUNT;
    }

    return {
      discounts: [
        {
          targets,
          value: {
            percentage: {
              value: configuration.percentage.toString()
            }
          }
        }
      ],
      discountApplicationStrategy: DiscountApplicationStrategy.First
    };
  };

  • 部署
cd ../.. && npm run deploy

Value

IDN折扣功能实现

Checkout UI extension

why

Checkout顾名思义就是下单,它承担了电商体系下最核心的支付任务.

Shopify在国外这么流行,除了低代码建站,扩展性强,多渠道销售这些优势以外,安全性强是不容忽视的重要特征.

Shopify之前为了保证电商支付的安全性,一直采用非常保守的策略.

在以前,整个Checkout功能都是以黑盒的形式包装了起来,开发者只能通过链接或者form表单的形式跳转到Shopify提供的支付网关,而接下来的所有用户行为都直接被Shopify接管.

ShopifyCheckout用黑盒的形式进行封装,一方面确实保证了支付的安全性,但另一方面也被众多开发者诟病.

开发者希望Shopify能在Checkout环节开放出更多的权限和接口,而这些开放的功能能打开大家的想象空间,能够在Checkout环节加入自定义个性化的功能设计,从而推动商品的营销.

开放虽然能促进多样性的发展,但同时也将安全性暴露在大众面前.Shopify历经了多年的打磨,在保证开放性和安全性共存的前提下,于2022年万众期待下高调的推出了Checkout UI extension.

WHAT

Checkout UI extension主要解决什么什么问题呢?

以前Shopify出于安全考虑,没有将Checkout开放出来给开发者使用.

如今如果有人想在Checkout环节开发个性化的功能,那么你可以依据Checkout UI extension的文档规范,以app的形式开发自定义功能并最终发布到应用商店.

最终商家通过安装app,就能在自己电商的Checkout加入自定义功能.

HOW

Checkout UI extension给了开发者能自定义功能的自由,但这个自由存在界限.

Checkout UI extension并没有将Checkout的源代码暴露出来,而是提供了一个个 Extension points.

Extension points类似于页面上的一个个插槽,而app开发的这些功能就能插入到这些插槽当中.

从上图中可以看出,Extension points用两种颜色标明.一类是蓝色,蓝色是Static extension points.而另一类是红色,红色是Dynamic extension points;

那怎么样去理解静态和动态区别呢?

静态扩展点是指Shopify在设计API时提前定义好的扩展点,这些扩展点可以被使用者自由添加或更改。使用静态扩展点的开发者可以在这些点上创建定制化的功能.

静态扩展点的位置是定死的,触发条件也是定死的,它会和具体的下单特征进行绑定.

动态扩展点的位置是活的,触发条件也是活的,它不会和具体的下单特征进行绑定.

代码实现

  • 创建扩展
npm run shopify app generate extension -- --type checkout_ui --name=my-checkout-ui-extension
  • 注册扩展点
// shopify.function.extension.toml

type = "checkout_ui_extension"
name = "my-checkout-ui-extension"

extension_points = [
  'Checkout::Dynamic::Render'
]

[capabilities]
api_access = true
  • 启动项目
yarn dev
  • 进入项目文件夹src/index.jsx里面编写插件的逻辑
import React, { useEffect, useState } from "react";
import {
  render,
  Banner
} from "@shopify/checkout-ui-extensions-react";

render("Checkout::Dynamic::Render", () => <App />);

function App() {
  const [showError, setShowError] = useState(false);
  return (
      {showError && (
      <Banner status="critical">
        There was an issue adding this product. Please try again.
      </Banner>
    )}
)}

  • 部署插件

npm run deploy

Value

优惠券

AU处方

pre-purchase product offers

survey-overview

分类:

前端

标签:

前端

作者介绍

Kay
V1