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 Senparc.Weixin SDK, which is shared on GitHub. This project is the most popular C# project on GitHub in China, with the most stars—1690 at the time this case study was published, with 1689 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. Large amounts of companies use WeChat as marketing channel. However, WeChat’s native backend management system is not user-friendly enough, especially the marketing part, like monitoring followers’ status, generating live campaign activity rules, DIYing campaign games. So customers often have to develop another management system by themselves. This will increase their cost and preparation time.

Senparc wanted to publish a system which can manage WeChat backend, monitor users’ status, DIY game flow and elastically increase different new games, simultaneously, and with more interaction. So customers can easily manage their WeChat account and set up WeChat live campaign.

Technical pain points

Both WeChat account backend and management client are websites. The marketing WeChat account will have high concurrent requests during live campaign. Senparc needs some convenient method to auto-scale instances and configure load balance. This will help Senparc decrease IT cost and focus more on development. Azure Web App is ideal for this scenario. Also, Senparc can use Azure Web App to easily manage websites’ hosting environment, remote debug, and quickly connect to SQL databases and Redis Cache.

Besides, Senparc is impressed by Cognitive Service. They want to integrate some intelligent games in this management system using Cognitive Service.

So Senparc plans to develop this system with Cognitive Service, and host its backend on Azure Web App for load balancing.

Solution, steps, and delivery

Solution delivery

Senparc and us worked together, delivered an end to end digital marketing WeChat management solution, including a WeChat frontend, a management portal, and auto-deployment ARM template.

The WeChat app includes some sample HTML 5 game pages to provide frontend experiences for users.

The WeChat management portal is for WeChat operators to monitor the information of WeChat followers, running status of games, and to generate parameters of intelligent campaign games which will be showed by WeChat web pages, based on Cognitive Service.

The auto-deploy template includes ARM template and its deployment project. We can use the deployment project to call ARM template and then create Azure App Service, SQL Database, Redis Cache and deploy the code package in any Azure subscription.

WeChat front-end H5 game page

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 Azure Web App, Azure SQL Database, Azure Redis Cache and Cognitive Service.

  • The WeChat HTML5 game pages are hosted on Azure Web App, and emotion competition and photo comparision features are implemented by Cognitive Service.
  • The WeChat mangement Portal is hosted on Azure Web App.
  • WeChat auth token and temp data are stored in Azure Redis Cache.
  • User data are stored in Azure SQL Database.

Technical architecture

Technical Architecture

Activities

Microsoft hosted two hackfests with Senparc. The first one was at Senparc company in Suzhou to train Senparc people Azure fundamental. The second one, we invited Senparc to BJW building, with Corp TE together to help them migrate their solution to Azure.

Hackfest photos

Hackfest Photo

Hackfest Photo

Hackfest Photo

Technical Implementation

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

Management portal code

WeChat back-end code

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

  1. Leverage the autoscale feature to handle the high concurrent requests when the WeChat compaign activities is being hosted. This decreases their IT operation efforts compared with using their local servers.
  2. We found that WeChat sometimes blocks the Azure domain name. We bind custom domain name with our Azure Web App to solve this issue.
  3. Leverage SSL feature to enable WeChat Pay in the solution (WeChat Pay is a payment method in WeChat)
    • Upload the .pfx format WeChat Pay certificate to Web App SSL certificate function.
    • Add certificate thumbprint to Azure App Settings to help code find the certificate uploaded
    • Modify the code as below to get 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();

Azure Web App

Azure Web App Capture

Azure Web App Capture

Azure Redis Cache and Azure SQL Database are served as data store.

Azure Storage

Azure Redis Cache Capture

Azure SQL Database

Azure SQL Database Capture

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

   "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 in Senparc’s web server. IT Pro can set up the environment and deploy the code by signing in using his Azure account, powered by Azure Active Directory. Part of the .NET code is as below:

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 technical engagement, Senparc positioned this WeChat digital marketing solution as their company’s software as a service (SaaS) product. This solution is entirely hosted on Microsoft Azure. The auto-scale capability of Azure Web App makes Senparc handle the high concurrent requests of digital marketing scenario better. The Cognitive Service makes online social games in their solution more creative.

One of Senparc’s customers, Suzhou broadcasting station, has adopted this solution in their 1048MHz channel (Traffic channel), to provide WeChat online interactive activities in the traffic rules education program.

This solution will also be promoted on Azure China official site, as one pilot of digital marketing solution based on WeChat and Azure, delivered by Microsoft partner. This promotion will help attract WeChat partners, in order to grow Azure business in China.

Great team

Special thanks to the Senparc team, the Microsoft China DX Technical Evangelist team, and the Microsoft Audience Evangelism team.

This 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