WeChat is the center of digital marketing in China. It has more than 500 million users, of which more than 50 percent open its app 10 times a day.

WeChat solutions can be hosted on the Web Apps feature of Microsoft Azure App Service. The ability to scale out and scale up makes Azure App Service ideal for WeChat live-campaign scenarios.

Microsoft teamed up with Senparc, a development partner of WeChat, to migrate the Senparc digital-marketing management system to Azure. Its back end is hosted on Web Apps and powered by Cognitive Services.

Customer profile

Senparc is a WeChat development company located in Suzhou, China. Its main businesses include architecture, development and implementation for e-commerce, and digital marketing for the WeChat app. Its customers include Fortune 500 companies such as General Electric, United Parcel Service, China Unicom, China Telecom, Tencent, and Unilever and many local government departments.

One of the main products that Senparc offers for WeChat is the Senparc.Weixin SDK, which is shared on GitHub (JeffreySu/WeiXinMPSDK). This project is the most popular C# project on GitHub in China, with 1690 stars at the time this case study was published and 1691 forks. It’s also the most famous C# SDK for WeChat development.

Problem statement

Business pain points

WeChat is the most widely used app in China, and many companies use WeChat as a marketing channel. However, the WeChat back-end management system is not particularly user-friendly, especially its marketing aspect: monitoring followers’ status, generating live campaign activity rules, creating campaign games, and so on. Customers often need to develop their own management systems, which increases their costs and preparation time.

Senparc wanted to publish a system that can manage the WeChat back end, monitor users’ status, enable DIY game flow, and manage the release of multiple new games simultaneously and with more interaction. With such a system, customers could easily manage their WeChat accounts and set up WeChat live campaigns.

Technical pain points

Both the WeChat account back end and management client are websites. The marketing WeChat account has high concurrent requests during live campaigns. Senparc needed a convenient method to auto-scale instances and configure load balancing. Doing so would help Senparc decrease IT costs and focus more on development. Web Apps is ideal for this scenario and also enables Senparc to easily manage the hosting environments of websites, perform remote debugging, and quickly connect to SQL databases and Redis Cache.

Senparc also wanted to integrate some intelligent games into this management system by using Cognitive Services.

Solution, steps, and delivery

Solution delivery

Senparc and Microsoft worked together to deliver an end-to-end marketing-management solution for WeChat, including a WeChat front end, a management portal, and an auto-deployment template for Azure Resource Manager.

  • The WeChat app includes some sample HTML5 game pages to provide front-end experiences for users.
  • The WeChat management portal is used by WeChat operators to track WeChat followers, monitor the running status of games, and generate the parameters of intelligent campaign games that are shown by WeChat web pages, based on Cognitive Services.
  • The auto-deployment template includes a Resource Manager template and its deployment project. We can use the deployment project to call the template and then create instances of App Service, SQL Database, and Redis Cache and deploy the code package in any Azure subscription.

WeChat front-end HTML5 game

WeChat game page

WeChat mangement portal user-monitor page

User-monitor page

WeChat mangement portal game-creation page

Game-creation page

Technical Architecture

This solution leverages Web Apps, SQL Database, Redis Cache, and Cognitive Services.

  • The WeChat HTML5 game pages are hosted on Web Apps; emotion competition and photo-comparision features are implemented by Cognitive Services.
  • The WeChat management portal is hosted on Web Apps.
  • The WeChat authorization token and temporary data are stored in Redis Cache.
  • User data are stored in SQL Database.

Technical architecture diagram

Activities

Microsoft hosted two hackfests with Senparc. The first was at Senparc headquarters in Suzhou to train Senparc personnel on Azure fundamentals. For the second hackfest, Microsoft invited Senparc to their Beijing West office, where Microsoft technical evangelists helped Senparc migrate their solution to Azure.

Hackfest photos

Hackfest montage

Hackfest team

Hackfest whiteboard

Technical implementation

After several months of work, we delivered the finished code, consisting of the WeChat management portal and game pages based on .NET and the WeChat API.

Management portal code

WeChat back-end code

All code is hosted on Web Apps with some advanced features being used.

  1. The autoscale feature handles high volumes of concurrent requests when WeChat compaign activities are hosted. This decreases IT operation efforts compared with using local servers.
  2. We discovered that WeChat sometimes blocks the Azure domain name. To solve this issue, we use Web Apps to bind custom domain names.
  3. The SSL feature enables WeChat Pay in the solution. (WeChat Pay is a payment method in WeChat.)
    • Upload the .pfx format WeChat Pay certificate to the Web Apps SSL certificate function.
    • Add the certificate thumbprint to the app settings to help the code find the uploaded certificate.
    • Modify the code as follows to locate the certificate by thumbprint.
      X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser);
      certStore.Open(OpenFlags.ReadOnly);
      X509Certificate2Collection certCollection = certStore.Certificates.Find(
                                 X509FindType.FindByThumbprint,
                                 "[Certificate Thumbprint]",
                                 false);
      if (certCollection.Count > 0)
      {
        X509Certificate2 cert = certCollection[0];
        Console.WriteLine(cert.FriendlyName);
      }
      certStore.Close();

Web Apps feature of Azure App Service

Azure App Service

SSL certificates page in Azure App Service

Redis Cache and SQL Database serve as data storage.

Azure Redis Cache

Azure Redis Cache

Azure SQL Database

Azure SQL Database

The auto-deployment template helps Senparc quickly set up the Azure environment. Part of the JSON file is as follows:

   "resources": [
    {
      "name": "[parameters('WebAppSvcPlanName')]",
      "type": "Microsoft.Web/serverfarms",
      "location": "[resourceGroup().location]",
      "apiVersion": "2014-06-01",
      "dependsOn": [ ],
      "tags": {
        "displayName": "AppSvcPlan"
      },
      "properties": {
        "name": "[parameters('WebAppSvcPlanName')]",
        "sku": "[parameters('WebAppSvcPlanSKU')]",
        "workerSize": "[parameters('WebAppSvcPlanWorkerSize')]",
        "numberOfWorkers": 1
      }
    },
    {
      "name": "[parameters('SQLServerName')]",
      "type": "Microsoft.Sql/servers",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "SqlServer"
      },
      "apiVersion": "2014-04-01-preview",
      "properties": {
        "administratorLogin": "[parameters('sqlAdministratorLogin')]",
        "administratorLoginPassword": "[parameters('sqlAdministratorLoginPassword')]",
        "version": "12.0"
      },
      "resources": [
        {
          "name": "[parameters('SQLDBName')]",
          "type": "databases",
          "location": "[resourceGroup().location]",
          "tags": {
            "displayName": "Database"
          },
          "apiVersion": "2014-04-01-preview",
          "dependsOn": [
            "[parameters('SQLServerName')]"
          ],
          "properties": {
            "edition": "Basic",
            "maxSizeBytes": "1073741824",
            "requestedServiceObjectiveName": "Basic"
          }
        },
        {
          "type": "firewallrules",
          "apiVersion": "2014-04-01-preview",
          "dependsOn": [
            "[parameters('SQLServerName')]"
          ],
          "location": "[resourceGroup().location]",
          "name": "AllowAllWindowsAzureIps",
          "properties": {
            "endIpAddress": "255.255.255.255",
            "startIpAddress": "0.0.0.0"
          }
        }
      ]
    },
    {
      "name": "[parameters('redisCacheName')]",
      "apiVersion": "2015-08-01",
      "type": "Microsoft.Cache/Redis",
      "location": "[resourceGroup().location]",
      "tags": {
        "displayName": "RedisCache"
      },
      "properties": {
        "enableNonSslPort": "[parameters('enableNonSslPort')]",
        "sku": {
          "capacity": "[parameters('redisCacheCapacity')]",
          "family": "[parameters('redisCacheFamily')]",
          "name": "[parameters('redisCacheSKU')]"
        }
      }
    },
    {
      "name": "[parameters('WebAppName')]",
      "type": "Microsoft.Web/sites",
      "location": "[resourceGroup().location]",
      "apiVersion": "2015-08-01",
      "dependsOn": [
        "[concat('Microsoft.Web/serverfarms/', parameters('WebAppSvcPlanName'))]"
      ],
      "tags": {
        "[concat('hidden-related:', resourceGroup().id, '/providers/Microsoft.Web/serverfarms/', parameters('WebAppSvcPlanName'))]": "Resource",
        "displayName": "WebApp"
      },
      "properties": {
        "name": "[parameters('WebAppName')]",
        "serverFarmId": "[resourceId('Microsoft.Web/serverfarms/', parameters('WebAppSvcPlanName'))]"
      },
      "resources": [
        {
          "apiVersion": "2015-08-01",
          "name": "web",
          "type": "config",
          "dependsOn": [
            "[concat('Microsoft.Web/sites/', parameters('WebAppName'))]"
          ],
          "tags": {
            "displayName": "WebAppConfig"
          },
          "properties": {
            "phpVersion": "5.6",
            "netFrameworkVersion": "v4.6",
            "use32BitWorkerProcess": false,
            "webSocketsEnabled": true,
            "alwaysOn": false,
            "remoteDebuggingEnabled": false
          }
        },
        {
          "name": "appsettings",
          "type": "config",
          "apiVersion": "2015-08-01",
          "dependsOn": [
            "[concat('Microsoft.Web/sites/', parameters('WebAppName'))]"
          ],
          "tags": {
            "displayName": "WebAppAppSettings"
          },
          "properties": {
            "EmotionKey": "[parameters('CognitiveEmotionKey')]",
            "VisionKey": "[parameters('CognitiveVisionKey')]",
            "DomainName": "[concat('http://',parameters('WebAppName'),'.chinacloudsites.cn')]",
            "AppId": "[parameters('wxAppId')]",
            "AppSecret": "[parameters('wxAppSecret')]",
            "ConnectionString": "[concat('data source=', reference(concat('Microsoft.Sql/servers/', parameters('SQLServerName'))).fullyQualifiedDomainName, ',1433;initial catalog=', parameters('SQLDBName'), ';User Id=', parameters('sqlAdministratorLogin'),';Password=', parameters('sqlAdministratorLoginPassword'), ';MultipleActiveResultSets=True;App=EntityFramework;')]",
            "Cache_Redis_Configuration": "[concat(parameters('redisCacheName'),'.redis.cache.chinacloudapi.cn:6380,abortConnect=false,ssl=true,password=', listKeys(resourceId('Microsoft.Cache/Redis', parameters('redisCacheName')), '2015-08-01').primaryKey)]"
          }
        },
        {
          "name": "MSDeploy",
          "type": "extensions",
          "location": "[resourceGroup().location]",
          "apiVersion": "2015-08-01",
          "dependsOn": [
            "[concat('Microsoft.Web/sites/', parameters('WebAppName'))]"
          ],
          "tags": {
            "displayName": "WebAppMSDeploy"
          },
          "properties": {
            "packageUri": "[variables('packageURI')]"
          }
        }
      ]
    }
  ]

The deployment function is integrated into the Senparc web server. An IT pro can set up the environment and deploy the code by signing in with an Azure account, powered by Azure Active Directory. Part of the .NET code is as follows:

public ActionResult AutoTry()
{
   ActionResult result = SignIn();
   return result;
}
public ActionResult SignIn()
{
   string stateMarker = Guid.NewGuid().ToString();
   string authorizationRequest = String.Format(
   "https://login.chinacloudapi.cn/common/oauth2/authorize?response_type=code&client_id={0}&resource={1}&redirect_uri={2}&state={3}",
    Uri.EscapeDataString("[clientid]]"),
    Uri.EscapeDataString("https://management.chinacloudapi.cn/"),
    Uri.EscapeDataString(this.Request.Url.GetLeftPart(UriPartial.Authority).ToString() + "/Deployment/ProcessCode"),
    Uri.EscapeDataString(stateMarker)
   );
   return new RedirectResult(authorizationRequest);
}
public string ProcessCode (string code, string error, string error_description, string resource, string state)
{
  var cc = new ClientCredential("[clientid]", "[clientsecret]]"); 
  var context = new AuthenticationContext("https://login.chinacloudapi.cn/common");
  AuthenticationResult token = null;
  token = context.AcquireTokenByAuthorizationCode(code,new Uri(Request.Url.GetLeftPart(UriPartial.Path)), cc);
}
public string DoDeploy(string token)
{
  //Deploy Parameters
  var groupName = "";
  var rgPara = new ResourceGroup("China North");
  var deploymentName = "";

  //Resource Parameters
    ...
  //Create Parameter Json
  string parametersJson = UpdateParameterJson(webAppName, sasToken, deployPackageURI, storageAccountName, webAppSvcPlanName, cognitiveKey, sqlDBName, sqlServerName, sqlAdministratorLogin, sqlAdministratorLoginPassword, wxAppId, wxAppSecret);
  var credential = new TokenCredentials(token);
  //Create ARM
  var dpResult = CreateTemplateDeploymentAsync(credential, rgPara, groupName, deploymentName, subscriptionId, parametersJson);            
  return "任务已成功提交";
}
public static async Task<DeploymentExtended> CreateTemplateDeploymentAsync(
        ServiceClientCredentials credential,
        ResourceGroup rgPara,
        string groupName,
        string deploymentName,
        string subscriptionId,
        string parametersJson)
{
   Console.WriteLine("Creating the template deployment...");
   var resourceManagementClient = new ResourceManagementClient(new Uri("https://management.chinacloudapi.cn/"), credential)
   { SubscriptionId = subscriptionId };
   DeploymentExtended aa = null;
   try
   {
     var result = resourceManagementClient.ResourceGroups.CreateOrUpdateAsync(groupName, rgPara).Result;
   }
   catch (Exception e)
   {
     Console.WriteLine(e.Message);
   }
   string filePath = AppDomain.CurrentDomain.BaseDirectory + "WebappTemplate.json";
   var deployment = new Deployment();
   deployment.Properties = new DeploymentProperties
   {
     Mode = DeploymentMode.Incremental,
     Template = System.IO.File.ReadAllText(filePath),
     Parameters = parametersJson
   };
   try
   {
     aa = await resourceManagementClient.Deployments.CreateOrUpdateAsync(
          groupName,
          deploymentName,
          deployment);
   }
   catch (Exception e)
   {
      Console.WriteLine(e.Message);
   }
   return aa;
}

Conclusion

As a result of this project, Senparc positioned this WeChat digital-marketing solution as a software as a service (SaaS) product. The solution is entirely hosted on Microsoft Azure. The auto-scale capability of Web Apps handles the high volumes of concurrent requests, and Cognitive Services enables greater creativity in the social games.

A Senparc customer, the Suzhou broadcasting station, adopted this solution for their traffic channel, providing WeChat interactive activities as part of their traffic-rules education program.

Special thanks to the Senparc team, the Microsoft China DX Technical Evangelist team, and the Microsoft Audience Evangelism team. The project team included the following participants:

  • Zhenwei Su – CEO, Senparc
  • Yunkun Fu – Developer, Senparc
  • Ellen Jin – UI Designer, Senparc
  • Malgosia Mazany – DX Audience Evangelism Manager, Microsoft
  • Yan Zhang – Audience Evangelism Manager, Microsoft China
  • Rita Zhang – DX Technical Evangelist, Microsoft
  • Bhargav Nookala – DX Technical Evangelist, Microsoft
  • Haishi Bai – DX Technical Evangelist, Microsoft
  • Qixiao Wang – DX Technical Evangelist, Microsoft China
  • Shijun Liu – DX Technical Evangelist, Microsoft China
  • David Yan – DX Technical Evangelist, Microsoft China