The author | meter source edge | alibaba cloud native public number

As the demographic dividend of the Internet has gradually weakened and the growth based on traffic has slowed down, the Internet industry urgently needs to find a new blue ocean capable of carrying its continuous growth. Industrial Internet is the new trend under this grand background. We see that the Internet wave is sweeping traditional industries. Cloud computing, big data and artificial intelligence have been integrated into the production links of finance, manufacturing, logistics, retail, entertainment, education, medical and other industries on a large scale. This integration is called industrial Internet. In the industrial Internet, SaaS is the middle force of ToB track, such as CRM, HRM, expense control system, financial system, collaborative office and so on.

Challenges for SaaS systems

In the era of consumer Internet, people are searching for what they want. Various manufacturers build services and ecosystems with maximum traffic on the basis of cloud computing, big data, artificial intelligence and other technologies, and build systems based on the logic of mass content distribution and traffic sharing. But in the era of industrial Internet, the supply relationship has changed, we are customized what we want, need to carry out two-way construction from both sides of supply and demand, at this time, the flexibility and scalability of the system is facing unprecedented challenges, especially in the field of SaaS ToB.

Especially for the current economic environment, SaaS vendors to understand, can no longer burn money, only focus on the number of their users, and more to think about how to help customers reduce costs, increase efficiency, so need to put more energy on the ability to customize their products.

How to Deal with challenges

Salesforce, the leader in the SaaS field, extends the concept of CRM to Marketing, Sales, and Service. Among these three fields, only Sales has specialized SaaS products, while the other two fields are industrial solutions of isVs in different industries. By what? Salesforce’s strong aPaaS platform, of course. Isvs, internal implementations and customers can build SaaS systems in their own industries and fields through aPaaS platforms in their respective dimensions to establish a complete ecosystem. So in my opinion, Salesforce has evolved from a SaaS company to an aPaaS platform company. This evolution process also confirms the conversion logic of consumer Internet and industrial Internet as well as the core appeal of the latter.

Not all SaaS companies have the money and time to incubate and polish their aPaaS platforms, but the market is changing and the demand is real. If you want to survive, change. The core of this change is to be able to make our current SaaS system flexible. Compared with aPaaS platform, which is difficult to build, we can actually choose the lightweight and effective Serverless solution to improve the flexibility and scalability of the existing system, so as to achieve different customized needs of users.

Serverless workflow

The concept of Serverless has been described in the previous article “Double Optimization of Resource Cost! See The Innovative practices of Serverless Subverting Programming education”, and the concepts and practices of Serverless functional computing (FC) have also been introduced. This article introduces service choreography, the Serverless workflow, which is the core element of building system flexibility.

Serverless Workflow is a fully managed cloud service for coordinating the execution of multiple distributed tasks. In the Serverless workflow, distributed tasks can be orchestrated sequentially, in branches, in parallel, and so on. The Serverless workflow reliably coordinates the execution of tasks according to set steps, tracks the state transitions of each task, and performs the retry logic you define when necessary to ensure that the workflow completes smoothly. Serverless Workflow makes it easy to diagnose and debug applications by providing logging and auditing to monitor workflow execution.

The following diagram illustrates how the Serverless workflow coordinates distributed tasks, which can be functions, integrated cloud service apis, or programs running on virtual machines or containers.

After reading the introduction of Serverless workflow, you may already have some ideas. At the heart of system flexibility and extensibility is choreography of services, both in the past BPM and now aPaaS. Therefore, the core idea of reconstructing SaaS system flexibility scheme based on Serverless workflow is to sort out, split and extract the functions that users want to customize most in the system, and then provide stateless capability with function calculation (FC). These function points are arranged through Serverless workflow. Thus realizing different business processes.

The flexible ordering module is built by calculating FC and Serverless workflow

I believe that we are familiar with the ordering scene, ordering take-out at home or ordering food in restaurants, are involved in this scene. There are also many SaaS service providers that provide ordering systems, and there are many good SaaS ordering systems. With the transformation from consumer Internet to industrial Internet, these SaaS ordering systems are facing more and more customized demands, one of which is that different merchants will display different payment methods in payment, such as Alipay, wechat Pay and UnionPay when ordering from A merchants. Alipay and JINGdong Pay are displayed when paying after ordering from B merchant. All of a sudden, Meituan Pay pops up again. At this time, B merchant accepts Meituan Pay, so alipay, JINGdong Pay and Meituan Pay will appear when paying from B merchant after ordering. There are more and more such customization requirements, and these SaaS products without PaaS platforms will be constantly hard-coded to meet the needs of different vendors, which is clearly not a sustainable model.

So let’s see how the FC and Serverless workflows can be elegantly solved using functions. Let’s start with the ordering process:

1. Create a process using Serverless Workflow

First, I need to transform the above user-side process into an application-side process, where I need to use the Serverless workflow to do this.

Open the Serverless console and create the order process. Here, Serverless Workflow uses the process definition language (FDL) to create the workflow. See the documentation for how to use FDL to create the workflow. The flow chart is as follows:

The FDL code is:

version: v1beta1
type: flow
timeoutSeconds: 3600
steps:
  - type: task
    name: generateInfo
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: products
        source: $input.products
      - target: supplier
        source: $input.supplier
      - target: address
        source: $input.address
      - target: orderNum
        source: $input.orderNum
      - target: type
        source: $context.step.name
    outputMappings:
      - target: paymentcombination
        source: $local.paymentcombination
      - target: orderNum
        source: $local.orderNum
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
  -type: task
    name: payment
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: orderNum
        source: $local.orderNum
      - target: paymentcombination
        source: $local.paymentcombination
      - target: type
        source: $context.step.name
    outputMappings:
      - target: paymentMethod
        source: $local.paymentMethod
      - target: orderNum
        source: $local.orderNum
      - target: price
        source: $local.price
      - target: taskToken
        source: $input.taskToken
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
  - type: choice
    name: paymentCombination
    inputMappings:
      - target: orderNum
        source: $local.orderNum
      - target: paymentMethod
        source: $local.paymentMethod
      - target: price
        source: $local.price
      - target: taskToken
        source: $local.taskToken
    choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price            
              - target: orderNum
                source: $input.orderNum
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken
      - condition: $.paymentMethod == "weixin"
        steps:
          - type: task
            name: weixin
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
      - condition: $.paymentMethod == "unionpay"
        steps:
          - type: task
            name: unionpay
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
    default:
      goto: orderCanceled
  - type: task
    name: orderCompleted
    resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/orderCompleted
    end: true
  - type: task
    name: orderCanceled
    resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/cancerOrder
Copy the code

Before I go through the whole process, I want to point out that we are not building the ordering module entirely through Serverless function computation and Serverless workflow, but just using it to solve the flexibility problem, so the main application of this example is written in Java, It then combines Serverless function calculation with Serverless workflow. Let’s examine this process in detail.

2. Start the process

Normally, the process should start at the beginning of the order, so in this example, MY design is to start the process after we have selected the product and business and filled in the address:

Here we start the process using OpenAPI provided by Serverless Workflow.

  • Java Startup Process

For this example I use the Java SDK for Serverless Workflow and first add the dependency to the POM file:

<dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> < version > [4.3.2, 5.0.0) < / version > < / dependency > < the dependency > < groupId > com. The aliyun < / groupId > < artifactId > aliyun - Java - SDK - FNF < / artifactId > < version > [1.0.0, 5.0.0) < / version > < / dependency >Copy the code

Then create the Config class that initializes the Java SDK:

@Configuration public class FNFConfig { @Bean public IAcsClient createDefaultAcsClient(){ DefaultProfile profile = DefaultProfile. GetProfile (cn - "XXX", / / region ID "ak", / / RAM account the AccessKey ID "sk"); // RAM account Access Key Secret IAcsClient client = new DefaultAcsClient(profile); return client; }}Copy the code

The startFNF method in Controller exposes the GET interface and takes three arguments:

  • Fnfname: Name of the process to be started.

  • Execuname: The name of the process instance after the process is started.

  • Input: start input parameters, such as service parameters.

   @GetMapping("/startFNF/{fnfname}/{execuname}/{input}")
    public StartExecutionResponse startFNF(@PathVariable("fnfname") String fnfName,
                                           @PathVariable("execuname") String execuName,
                                           @PathVariable("input") String inputStr) throws ClientException {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("fnfname", fnfName);
        jsonObject.put("execuname", execuName);
        jsonObject.put("input", inputStr);
        return fnfService.startFNF(jsonObject);
    }
Copy the code

Consider the startFNF method in the Service, which has two parts. The first part is the start process, and the second part is the creation of the order object and the simulated repository (in our example, it is placed in the Map) :

    @Override
    public StartExecutionResponse startFNF(JSONObject jsonObject) throws ClientException {
        StartExecutionRequest request = new StartExecutionRequest();
        String orderNum = jsonObject.getString("execuname");
        request.setFlowName(jsonObject.getString("fnfname"));
        request.setExecutionName(orderNum);
        request.setInput(jsonObject.getString("input"));
 
        JSONObject inputObj = jsonObject.getJSONObject("input");
        Order order = new Order();
        order.setOrderNum(orderNum);
        order.setAddress(inputObj.getString("address"));
        order.setProducts(inputObj.getString("products"));
        order.setSupplier(inputObj.getString("supplier"));
        orderMap.put(orderNum, order);
 
        return iAcsClient.getAcsResponse(request);
    }
Copy the code

When starting the process, the process name and the name of the start process instance are the parameters that need to be passed in, and HERE I use the order number of each time as the start process instance name. For Input, you can construct a JSON string to pass in as required. Here I construct JSON strings for the product, merchant, address, and order number to pass into the process when it starts.

In addition, the Order instance of this Order was created and stored in Map to simulate and put into the library. In the subsequent links, the Order instance would be queried to update the Order attributes.

  • VUE select merchandise/merchant page

I used VUE to build the front end. After clicking the next step in the page of selecting commodities and merchants, I called the HTTP interface /startFNF/{fnfname}/{execuname}/{input} through GET. Corresponds to the Java method above.

  • Fnfname: Name of the process to be started.

  • Execuname: Randomly generates the UUID as the order number and as the name of the startup process instance.

  • Input: The product, merchant, order number, and address are constructed as JSON strings and passed into the process.

submitOrder(){ const orderNum = uuid.v1() this.$axios.$get('/startFNF/OrderDemo-Jiyuan/'+orderNum+'/{\n' + ' "products":  "'+this.products+'",\n' + ' "supplier": "'+this.supplier+'",\n' + ' "orderNum": "'+orderNum+'",\n' + ' "address": "'+this.address+'"\n' + '}' ).then((response) => { console.log(response) if(response.message == "success"){ this.$router.push('/orderdemo/' + orderNum) } }) }Copy the code

3. GenerateInfo node

The first object is generateInfo, so let’s see what FDL means:

  - type: task
    name: generateInfo
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/generateInfo-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: products
        source: $input.products
      - target: supplier
        source: $input.supplier
      - target: address
        source: $input.address
      - target: orderNum
        source: $input.orderNum
      - target: type
        source: $context.step.name
    outputMappings:
      - target: paymentcombination
        source: $local.paymentcombination
      - target: orderNum
        source: $local.orderNum
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
Copy the code
  • Name: indicates the node name.

  • TimeoutSeconds: specifies the timeout period. The length of time the node waits before jumping to an orderCanceled node pointed to by the goto branch.

  • Pattern: Set to waitForCallback to wait for confirmation. InputMappings: Specifies the connection parameters of this node.

    • TaskToken: Serverless Token automatically generated by workflow.
    • B: Yes, I do.
    • Supplier: The selected vendor.
    • Address: address of delivery.
    • OrderNum: Order number.
  • OutputMappings: The connection parameters of this node.

    • Paymentcombination: Payment method supported by this merchant.
    • OrderNum: Order number.
  • Catch: To catch an exception and jump to another branch.

Here resourceArn and serviceParams need to be explained separately. Serverless workflow supports integration with multiple cloud services, that is, treating other services as execution units of task steps. Service integration mode is expressed by FDL language. In task steps, resourceArn can be used to define the target service for integration, and pattern can be used to define the integration mode. So you can see in resourceArn configured in the acs: MNS: : : / switchable viewer/generateInfo – FNF – demo – jiyuan/messages information, namely the integration in the generateInfo node MNS message queue service, When generateInfo is triggered, a message is sent to GenerateInfo-fnF-Demo-Jiyuantopic. The message body and parameters are then specified in the serviceParams object. The MessageBody is the MessageBody, and the $configuration indicates that the MessageBody is generated through the input mapping inputMappings.

After looking at the first node example, you can see that in the Serverless workflow, information transfer between nodes can be transmitted through integrated MNS sending messages, which is one of the more widely used methods.

4. GenerateInfo FNF – demo function

The message sent to GenerateInfo-FNF-Demo-Jiyuantopic contains the product information, merchant information, address, and order number, indicating the beginning of an order placing process. Since the message is sent, the message must be received for subsequent processing. So open the function computing console, create the service, and create the event trigger function named Generateinfo-fnF-demo under the service. Select Python Runtime:

To create an MNS trigger, select Listen generateInfo-fnF-demo-Jiyuantopic.

Open the message service MNS console and create GenerateInfo-fnF-demo-jiyuantopic:

With the function ready, let’s start writing code:

# -*- coding: utf-8 -*- import logging import json import time import requests from aliyunsdkcore.client import AcsClient from aliyunsdkcore.acs_exception.exceptions import ServerException from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest def handler(event, context): # 1. Client region = "CN-Hangzhou" account_id = "XXXX" ak_id = "XXX" ak_secret = "XXX" fnf_Client = AcsClient( Ak_id, ak_secret, region) logger = logging.getLogger() # 2. The event is received in Topic GenerateInfo-fnF-demo-jiyuan. Logger.info ("products:" + bodyJson[" Products "]) Logger.info (" Supplier :" + bodyJson["supplier"]) logger.info("address:" + bodyJson["address"]) logger.info("taskToken:" + bodyJson["taskToken"]) supplier = bodyJson["supplier"] taskToken = bodyJson["taskToken"] orderNum = bodyJson["orderNum"] # 3. To determine which merchant is using which combination of payment methods, the example here is simple and crude. Normally, you should use metadata configuration to obtain paymentCombination = "" if supplier == "haidilao": paymentcombination = "zhifubao,weixin" else: paymentcombination = "zhifubao,weixin,unionpay" # 4. Call the Java service exposed interface, update the order information, Mainly is to update the payment url = "http://xx.xx.xx.xx:8080/setPaymentCombination/" + + "/" orderNum + paymentcombination + x = "/ 0" requests.get(url) # 5. Output = "{\"orderNum\": \"%s\", \"paymentcombination\":\"%s\" " \ "}" % (orderNum, paymentcombination) request = ReportTaskSucceededRequest.ReportTaskSucceededRequest() request.set_Output(output) request.set_TaskToken(taskToken) resp = fnf_client.do_action_with_exception(request) return 'hello world'Copy the code

Because the generateinfo-fnF-demo function is configured with MNS triggers, the generateinfo-fnF-demo function is triggered when topicGenerateinfo-fnf-demo-jiyuan receives a message.

The whole code is divided into five parts:

  • Build the Serverless Workflow Client.

  • The information in the event is the message content in topicGenerateInfo-fnF-Demo-Jiyuan, which is converted into a Json object.

  • To determine which merchants use which combination of payment methods, the example here is simple and crude, and normally it should be obtained using metadata configuration. For example, there is a merchant information configuration function in the system. You can configure the payment methods supported by the merchant on the interface to form metadata configuration information and provide a query interface for query.

  • Call the Java service exposed interface to update the order information, mainly the payment method.

  • Give the generateInfo node a response and return the data, which returns the order number and payment method. Because the pattern of this object is waitForCallback, you need to wait for the response result.

5. Payment node

Let’s look at the second node payment, starting with the FDL code:

- type: task
    name: payment
    timeoutSeconds: 300
    resourceArn: acs:mns:::/topics/payment-fnf-demo-jiyuan/messages
    pattern: waitForCallback
    inputMappings:
      - target: taskToken
        source: $context.task.token
      - target: orderNum
        source: $local.orderNum
      - target: paymentcombination
        source: $local.paymentcombination
      - target: type
        source: $context.step.name
    outputMappings:
      - target: paymentMethod
        source: $local.paymentMethod
      - target: orderNum
        source: $local.orderNum
      - target: price
        source: $local.price
      - target: taskToken
        source: $input.taskToken
    serviceParams:
      MessageBody: $
      Priority: 1
    catch:
      - errors:
          - FnF.TaskTimeout
        goto: orderCanceled
Copy the code

When the process flows to the Payment node, it means that the user enters the payment page.

The Payment node sends a message to the MNS topicPayment-FnF-demo-jiyuan, which triggers the payment-FnF-demo function.

6. Payment – FNF – demo function

The payment-fnF-demo function is created in the same way as the Generateinfo-fnf-demo function, which is no longer redundant. Let’s go straight to the code:

# -*- coding: utf-8 -*-
import logging
import json
import os
import time
import logging
from aliyunsdkcore.client import AcsClient
from aliyunsdkcore.acs_exception.exceptions import ServerException
from aliyunsdkcore.client import AcsClient
from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest
from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest
from mns.account import Account  # pip install aliyun-mns
from mns.queue import *
 
 
def handler(event, context):
    logger = logging.getLogger()
    region = "xxx"
    account_id = "xxx"
    ak_id = "xxx"
    ak_secret = "xxx"
    mns_endpoint = "http://your_account_id.mns.cn-hangzhou.aliyuncs.com/"
    queue_name = "payment-queue-fnf-demo"
    my_account = Account(mns_endpoint, ak_id, ak_secret)
    my_queue = my_account.get_queue(queue_name)
    # my_queue.set_encoding(False)
    fnf_client = AcsClient(
        ak_id,
        ak_secret,
        region
    )
    eventJson = json.loads(event)
 
    isLoop = True
    while isLoop:
        try:
            recv_msg = my_queue.receive_message(30)
            isLoop = False
            # body = json.loads(recv_msg.message_body)
            logger.info("recv_msg.message_body:======================" + recv_msg.message_body)
            msgJson = json.loads(recv_msg.message_body)
            my_queue.delete_message(recv_msg.receipt_handle)
            # orderCode = int(time.time())
            task_token = eventJson["taskToken"]
            orderNum = eventJson["orderNum"]
            output = "{\"orderNum\": \"%s\", \"paymentMethod\": \"%s\", \"price\": \"%s\" " \
                         "}" % (orderNum, msgJson["paymentMethod"], msgJson["price"])
            request = ReportTaskSucceededRequest.ReportTaskSucceededRequest()
            request.set_Output(output)
            request.set_TaskToken(task_token)
            resp = fnf_client.do_action_with_exception(request)
        except Exception as e:
            logger.info("new loop")
    return 'hello world'
Copy the code

The core idea of this function is to wait for the user to select a payment method on the payment page to confirm payment. So the QUEUE of MNS is used here to simulate waiting. It waits to receive messages in the payment-queue-fnF-demo, and returns the order number, payment method and amount selected by the user to the Payment node after receiving the message.

7. VUE page for selecting payment method

After passing through the generateInfo node, the payment method information of the order is already available, so for the user, after filling in the product, merchant and address, the page to jump to is the confirmation payment page, and includes the payment method supported by the merchant.

When entering the page, the interface exposed by the Java service is requested to obtain the order information and display different payment methods according to the payment method on the page. The code snippet is as follows:

When the user selects a payment method and clicks the submit order button, a message is sent to the payer-queue-fnF-Demo queue to inform the payer-FnF-Demo function to continue with the subsequent logic.

Here I use an HTTP trigger type function to implement the logic of sending messages to MNS. The paymentMethod-FnF-demo function code is as follows.

# -*- coding: utf-8 -*- import logging import urllib.parse import json from mns.account import Account # pip install aliyun-mns from mns.queue import * HELLO_WORLD = b'Hello world! \n' def handler(environ, start_response): logger = logging.getLogger() context = environ['fc.context'] request_uri = environ['fc.request_uri'] for k, v in environ.items(): if k.startswith('HTTP_'): # process custom request headers pass try: request_body_size = int(environ.get('CONTENT_LENGTH', 0)) except (ValueError): request_body_size = 0 request_body = environ['wsgi.input'].read(request_body_size) paymentMethod = urllib.parse.unquote(request_body.decode("GBK")) logger.info(paymentMethod) paymentMethodJson = json.loads(paymentMethod) region = "cn-xxx" account_id = "xxx" ak_id = "xxx" ak_secret = "xxx" mns_endpoint = "http://your_account_id.mns.cn-hangzhou.aliyuncs.com/" queue_name = "payment-queue-fnf-demo" my_account = Account(mns_endpoint, ak_id, ak_secret) my_queue = my_account.get_queue(queue_name) output = "{\"paymentMethod\": \"%s\", \"price\":\"%s\" " \ "}" % (paymentMethodJson["paymentMethod"], paymentMethodJson["price"]) msg = Message(output) my_queue.send_message(msg) status = '200 OK' response_headers = [('Content-type', 'text/plain')] start_response(status, response_headers) return [HELLO_WORLD]Copy the code

The logic of this function is simple: it sends the payment method and amount selected by the user to the MNS queue payment-queue-fnF-demo. The VUE code snippet is as follows:

8. PaymentCombination node

The paymentCombination node is a routing node that routes to different nodes by determining a parameter, and paymentMethod is naturally used as the judgment condition. The FDL code is as follows:

- type: choice
    name: paymentCombination
    inputMappings:
      - target: orderNum
        source: $local.orderNum
      - target: paymentMethod
        source: $local.paymentMethod
      - target: price
        source: $local.price
      - target: taskToken
        source: $local.taskToken
    choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price            
              - target: orderNum
                source: $input.orderNum
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken
      - condition: $.paymentMethod == "weixin"
        steps:
          - type: task
            name: weixin
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/weixin-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
      - condition: $.paymentMethod == "unionpay"
        steps:
          - type: task
            name: unionpay
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan.LATEST/functions/union-fnf-demo
            inputMappings:
            - target: price
              source: $input.price            
            - target: orderNum
              source: $input.orderNum
            - target: paymentMethod
              source: $input.paymentMethod
            - target: taskToken
              source: $input.taskToken
    default:
      goto: orderCanceled
Copy the code

The process here is that the user selects the payment method, sends the message to the payment-FnF-Demo function, and then returns the payment method, which is then sent to the paymentCombination node to determine the payment method and then to the node and function that processes the payment logic.

9. The zhifubao node

Let’s look specifically at a Zhifubao node:

    choices:
      - condition: $.paymentMethod == "zhifubao"
        steps:
          - type: task
            name: zhifubao
            resourceArn: acs:fc:cn-hangzhou:your_account_id:services/FNFDemo-jiyuan/functions/zhifubao-fnf-demo
            inputMappings:
              - target: price
                source: $input.price            
              - target: orderNum
                source: $input.orderNum
              - target: paymentMethod
                source: $input.paymentMethod
              - target: taskToken
                source: $input.taskToken
Copy the code

The resourceArn of this node is different from that of the previous two nodes. Here, ARN of function calculation is configured, that is, when the flow to this node will trigger zhifubao-FnF-demo function, which is an event firing function, but does not need to create any triggers. The process passes the order amount, order number, and payment method to the zhifubao-FSF-demo function.

10. Zhifubao FNF – demo function

Now let’s look at the code of zhifubao-fnF-demo function:

# -*- coding: utf-8 -*- import logging import json import requests import urllib.parse from aliyunsdkcore.client import AcsClient from  aliyunsdkcore.acs_exception.exceptions import ServerException from aliyunsdkfnf.request.v20190315 import ReportTaskSucceededRequest from aliyunsdkfnf.request.v20190315 import ReportTaskFailedRequest def handler(event, context): region = "cn-xxx" account_id = "xxx" ak_id = "xxx" ak_secret = "xxx" fnf_client = AcsClient( ak_id, ak_secret, region ) logger = logging.getLogger() logger.info(event) bodyJson = json.loads(event) price = bodyJson["price"] taskToken = bodyJson["taskToken"] orderNum = bodyJson["orderNum"] paymentMethod = bodyJson["paymentMethod"] Logger.info ("price:" + price) newPrice = int(price) * 0.8 Logger.info ("newPrice:" + STR (newPrice)) URL = "http://xx.xx.xx.xx:8080/setPaymentCombination/" + orderNum + "/" + paymentMethod + "/" + str(newPrice) x = requests.get(url) return {"Status":"ok"}Copy the code

The code logic in the example is simple: after receiving the amount, discount it by 20% and then update the price back to the order. The nodes and functions of other payment methods do the same and change the implementation logic. In this example, wechat Pay is 50% off and UnionPay is 30% off.

11. Complete the process

OrderCompleted and orderCanceled nodes in the flow don’t do much logic, so you can make your own use of the same logic as the previous nodes. So the complete process looks like this:

The node flow seen from the Serverless workflow looks like this:

conclusion

At this point, our example order module based on Serverless workflow and Serverless function calculation is complete. In the example, there are two points that need to be noted:

  • Configure metadata rules for merchants and payment methods.

  • Confirm the metadata rules for the payment page.

In actual production, we need to abstract the customizable parts into metadata description, and there needs to be a configuration interface to formulate the payment method of merchants, namely, update the metadata rules, and then the front-end page displays the corresponding content based on the metadata information.

Therefore, if you need to access other payment methods later, you only need to determine the routing rules in the paymentCombination routing node and add the corresponding payment method function. By adding a metadata configuration item, the new payment method can be displayed on the page and routed to the function that handles the new payment method.

The above content serves as a starting point to explore Serverless application scenarios that address the flexibility and scalability pain points of SaaS vendors. If you have any questions, you can also join the Nail group: 35712134 to find the answer, we will be there!

Course recommended

In order for more developers to enjoy the dividends brought by Serverless, this time, we gathered 10+ Technical experts in the field of Serverless from Alibaba to create the most suitable Serverless open course for developers to learn and use immediately. Easily embrace the new paradigm of cloud computing – Serverless.

Click to free courses: developer.aliyun.com/learning/ro…