{"id":19303100,"url":"https://github.com/asc-lab/azure-functions-billing","last_synced_at":"2025-08-01T03:04:47.024Z","repository":{"id":71724896,"uuid":"150387830","full_name":"asc-lab/azure-functions-billing","owner":"asc-lab","description":"Azure Functions v2 with .NET Core - billing in serverless architecture.","archived":false,"fork":false,"pushed_at":"2018-12-21T08:14:07.000Z","size":1016,"stargazers_count":61,"open_issues_count":1,"forks_count":41,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-07-23T02:27:51.967Z","etag":null,"topics":["azure-blob","azure-functions","azure-queue","azure-table","billing","blob","cosmosdb","cosmosdb-database","csv","insurance","jsreport","sendgrid","serverless","twilio"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/asc-lab.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2018-09-26T07:42:10.000Z","updated_at":"2023-12-28T02:26:41.000Z","dependencies_parsed_at":"2023-06-25T21:01:41.232Z","dependency_job_id":null,"html_url":"https://github.com/asc-lab/azure-functions-billing","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/asc-lab/azure-functions-billing","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-lab%2Fazure-functions-billing","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-lab%2Fazure-functions-billing/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-lab%2Fazure-functions-billing/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-lab%2Fazure-functions-billing/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/asc-lab","download_url":"https://codeload.github.com/asc-lab/azure-functions-billing/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/asc-lab%2Fazure-functions-billing/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":268162340,"owners_count":24205700,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-01T02:00:08.611Z","response_time":67,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["azure-blob","azure-functions","azure-queue","azure-table","billing","blob","cosmosdb","cosmosdb-database","csv","insurance","jsreport","sendgrid","serverless","twilio"],"created_at":"2024-11-09T23:25:03.135Z","updated_at":"2025-08-01T03:04:47.001Z","avatar_url":"https://github.com/asc-lab.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Azure Functions v2 with .NET Core\n\nThis example shows simplified billing system in serverless architecture.\n\nComprehensive guide describing exactly the architecture, applied design patterns and technologies can be found on our blog in article **[Azure Functions 2.0 – real world use case for serverless architecture](https://altkomsoftware.pl/en/blog/azure-functions/)**.\n\n**We encourage you to read, because in this README there is only a substitute for all information.**\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"Architecture\" src=\"https://raw.githubusercontent.com/asc-lab/dotnetcore-azure-functions/master/readme-images/azure-functions-architecture.png\" /\u003e\n\u003c/p\u003e\n\n1. User uploads CSV file (with name structure ```CLIENTCODE_YEAR_MONTH_activeList.txt.```) with Beneficiaries (the sample file is located in the ```data-examples``` folder) to a specific data storage - ```active-lists``` Azure Blob Container.\n\n2. The above action triggers a function (```GenerateBillingItemsFunc```) that is responsible for:\n    * generating billing items (using prices from an external database - CosmosDB ```crm``` database, ```prices``` collection) and saving them in the table ```billingItems```;\n    * sending message about the need to create a new invoice to ```invoice-generation-request```;\n\n3. When a new message appears on the queue ```invoice-generation-request```, next function is triggered (```GenerateInvoiceFunc```). This function creates domain object ```Invoice``` and save this object in database (CosmosDB ```crm``` database, ```invoices``` collection) and send message to queues: ```invoice-print-request``` and ```invoice-notification-request```.\n\n4. When a new message appears on the queue ```invoice-print-request```, function ```PrintInvoiceFunc``` is triggered. This function uses external engine to PDF generation - JsReport and saves PDF file in BLOB storage.\n\n5. When a new message appears on the queue ```invoice-notification-request```, function ```NotifyInvoiceFunc``` is triggered. This function uses two external systems - SendGrid to Email sending and Twilio to SMS sending.\n\n## Tutorial from scratch to run locally\n\n1. Install and run [Microsoft Azure Storage Emulator](https://docs.microsoft.com/en-us/azure/storage/common/storage-use-emulator).\n\n2. Install and run [CosmosDB Emulator](https://docs.microsoft.com/en-us/azure/cosmos-db/local-emulator). Check this on ```https://localhost:8081/_explorer/index.html```.\n\n3. Create in Emulator blob Container ```active-lists```.\n\n4. Upload  ```ASC_2018_02_activeLists.txt``` file from ```data-examples``` folder to ```active-lists``` blob.\n\n5. Create CosmosDB database ```crm``` and in this database create collections: ```prices```,  ```invoices```.\n\n6. Add CosmosDB properties ```PriceDbUrl``` and ```PriceDbAuthKey``` to ```local.appsettings.json``` in ```PriceDbInitializator``` and ```GenerateBillingIemsFunc```. You can copy this properties from ```Azure CosmosDB Emulator``` - check point 2 (URI and Primary Key).\n\n7. Run project ```PriceDbInitializator``` to init collection ```prices``` in ```crm``` database.\n\n8. Add CosmosDB connection string as ```cosmosDb``` to ```local.settings.json``` in ```GenerateInvoiceFunc```. You can copy this string from ```Azure CosmosDB Emulator``` - check point 2 (Primary Connection String).\n\n9. Create an account in [SendGrid](https://sendgrid.com/) and add property ```SendGridApiKey``` to ```local.settings.json``` in ```NotifyInvoiceFunc```.\n\n10. Create an account in [Twilio](https://www.twilio.com/) and add properties ```TwilioAccountSid``` ```TwilioAuthToken``` to ```local.settings.json``` in ```NotifyInvoiceFunc```.\n\n11. Run JsReport with Docker: ```docker run -p 5488:5488 jsreport/jsreport```. Check JsReport Studio on ```localhost:5488```.\n\n12. Add JsReport url as ```JsReportUrl``` to ```local.settings.json``` in ```PrintInvoiceFunc``` project.\n\n13. Add JsReport template with name ```INVOICE``` and content:\n\n```html\n\u003ch1\u003eInvoice {{invoiceNumber }}\u003c/h1\u003e\n\u003ch3\u003eCustomer: {{ customer }}\u003c/h3\u003e\n\u003ch3\u003eAddress: 00-101 Warszawa, Chłodna 21\u003c/h3\u003e\n\u003ch3\u003eDescription: {{ description }}\u003c/h3\u003e\n\n\u003ch3\u003eDetails:\u003c/h3\u003e\n\u003ctable width=\"90%\" border=\"1\" bgcolor=\"#C0C0C0\" align=\"center\"\u003e\n    \u003ctr\u003e\n        \u003cth\u003eItem\u003c/th\u003e\n        \u003cth\u003ePrice\u003c/th\u003e\n    \u003c/tr\u003e\n\n    {{#each lines}}\n    \u003ctr\u003e\n        \u003ctd\u003e{{ itemName }}\u003c/td\u003e\n        \u003ctd align=\"right\"\u003e{{ cost }}\u003c/td\u003e\n    \u003c/tr\u003e\n    {{/each}}\n\n    \u003ctr\u003e\n        \u003ctd\u003e\n            \u003cstrong\u003eTotal\u003c/strong\u003e\n        \u003c/td\u003e\n        \u003ctd align=\"right\"\u003e\n            \u003cstrong\u003e{{ totalCost }}\u003c/strong\u003e\n        \u003c/td\u003e\n    \u003c/tr\u003e\n\u003c/table\u003e\n```\n\nExample JSON for INVOICE template:\n\n```json\n{\n  \"customer\": \"ASC\",\n  \"invoiceNumber\": \"ASC/10/2018\",\n  \"description\": \"Invoice for insurance policies for 10/2018\",\n  \"lines\": [\n    {\n      \"itemName\": \"Policy A\",\n      \"cost\": 2140.0\n    },\n    {\n      \"itemName\": \"Policy B\",\n      \"cost\": 1360.0\n    }\n  ],\n  \"totalCost\": 3500.0\n}\n```\n\nAll properties in one `local.appsettings.json`:\n\n```\n{\n  \"IsEncrypted\": false,\n  \"Values\": {\n    \"FUNCTIONS_WORKER_RUNTIME\": \"dotnet\",\n    \"AzureWebJobsStorage\": \"UseDevelopmentStorage=true\",\n    \"PriceDbUrl\": \"https://localhost:8081\",\n    \"PriceDbAuthKey\": \"AUTH_KEY\",\n    \"cosmosDb\": \"AccountEndpoint=https://localhost:8081/;AccountKey=AUTH_KEY\",\n    \"JsReportUrl\": \"http://localhost:5488\",\n    \"SendGridApiKey\": \"SEND_GRID_API_KEY\",\n    \"TwilioAccountSid \": \"TWILIO_ACCOUNT_SID\",\n    \"TwilioAuthToken\": \"TWILIO_AUTH_TOKEN\"\n  }\n}\n```\n\n## Monitoring examples\n\nApplication Map for all function in one project:\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"Application Map 1\" \n    src=\"https://raw.githubusercontent.com/asc-lab/dotnetcore-azure-functions/master/readme-images/application_map_one_project.png\" /\u003e\n\u003c/p\u003e\n\nApplication Map for functions in separated projects:\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"Application Map 2\"\n    src=\"https://raw.githubusercontent.com/asc-lab/dotnetcore-azure-functions/master/readme-images/application_map_separated_projects.png\" /\u003e\n\u003c/p\u003e\n\nEnd-to-end transaction details:\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"End to End Transaction Details\"\n    src=\"https://raw.githubusercontent.com/asc-lab/dotnetcore-azure-functions/master/readme-images/performance.png\" /\u003e\n\u003c/p\u003e\n\n## Tips \u0026 Tricks\n\n1. CSV file is working for client code ```ASC``` (filename: ```ASC_2018_12_activeList.txt```). If you want run functions for another client code, you must simulate prices in database. Check project ```PriceDbInitializator```, file ```Program.cs```, method ```AddDoc```.\n\n2. Remember that you must use **Twilio Test Credentials**.\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"Twilio Test Credentials\" src=\"https://raw.githubusercontent.com/asc-lab/dotnetcore-azure-functions/master/readme-images/twilio_test_credentials.png\" /\u003e\n\u003c/p\u003e\n\n3. **Microsoft Azure Storage Emulator** with all created storages:\n\n\u003cp align=\"center\"\u003e\n    \u003cimg alt=\"Microsoft Azure Storage Emulator\" src=\"https://raw.githubusercontent.com/asc-lab/dotnetcore-azure-functions/master/readme-images/azure_storage_emulator.png\" /\u003e\n\u003c/p\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasc-lab%2Fazure-functions-billing","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fasc-lab%2Fazure-functions-billing","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fasc-lab%2Fazure-functions-billing/lists"}