Skip to content

Commit 9be991d

Browse files
committed
Add cross-framework Chargify config abstraction and .NET 4.5.2 support
Introduces IChargifyConfig and IChargifyAccountConfig interfaces for unified configuration access across .NET Framework and .NET Core/8+, with implementations for both (ChargifyConfigSection for .NET Framework, ChargifyConfig for .NET 8+). Refactors configuration classes and updates usages to support dependency injection and easier configuration retrieval. Adds net452 target to ChargifyDotNet, updates ConsumerApp.Net452 to use .NET Framework and new config, and improves nullability annotations. Also includes a workload-install.ps1 script.
1 parent e8fa9d7 commit 9be991d

15 files changed

+715
-61
lines changed

Source/ChargifyDotNet/ChargifyConnect.cs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ namespace ChargifyNET
4848
using System.Xml;
4949
using System.Xml.Linq;
5050
using ChargifyDotNet;
51+
using ChargifyNET.Configuration;
5152
using Json;
5253

5354
#endregion
@@ -69,11 +70,24 @@ public class ChargifyConnect : IChargifyConnect
6970
private int _timeout = 180000;
7071

7172
/// <summary>
72-
/// Constructor
73+
/// Initializes a new instance of the ChargifyConnect class.
7374
/// </summary>
7475
public ChargifyConnect()
7576
{ }
7677

78+
/// <summary>
79+
/// Initializes a new instance of the ChargifyConnect class using the specified account configuration.
80+
/// </summary>
81+
/// <remarks>This constructor simplifies initialization by allowing all required connection
82+
/// settings to be provided through a single configuration object. The values from the configuration are used to
83+
/// set up the connection to the Chargify service.</remarks>
84+
/// <param name="config">An object that provides the site, API key, API password, and shared key required to connect to the Chargify
85+
/// API. Cannot be null.</param>
86+
public ChargifyConnect(IChargifyAccountConfig config)
87+
: this(config.Site, config.ApiKey, config.ApiPassword, config.SharedKey)
88+
{
89+
}
90+
7791
/// <summary>
7892
/// Constructor
7993
/// </summary>
@@ -110,7 +124,7 @@ public ChargifyConnect(string url, string apiKey, string password, string shared
110124
string.Format("Chargify.NET Client v" +
111125
System.Reflection.Assembly.GetExecutingAssembly().GetName().Version);
112126

113-
private static string s_userAgent;
127+
private static string? s_userAgent;
114128

115129
/// <summary>
116130
/// Get or set the API key
@@ -200,12 +214,12 @@ public bool HasConnected
200214
/// <summary>
201215
/// Caller can plug in a delegate for logging raw Chargify requests
202216
/// </summary>
203-
public Action<HttpRequestMethod, string, string> LogRequest { get; set; }
217+
public Action<HttpRequestMethod, string, string>? LogRequest { get; set; }
204218

205219
/// <summary>
206220
/// Caller can plug in a delegate for logging raw Chargify responses
207221
/// </summary>
208-
public Action<HttpStatusCode, string, string> LogResponse { get; set; }
222+
public Action<HttpStatusCode, string, string>? LogResponse { get; set; }
209223

210224
/// <summary>
211225
/// Get a reference to the last Http Response from the chargify server. This is set after every call to

Source/ChargifyDotNet/ChargifyDotNet.csproj

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
4-
<TargetFrameworks>netstandard2.0;net8.0</TargetFrameworks>
4+
<TargetFrameworks>net452;netstandard2.0;net8.0</TargetFrameworks>
55
<Nullable>enable</Nullable>
66
<LangVersion>latest</LangVersion>
77

@@ -84,6 +84,9 @@
8484
<Reference Include="System.Security" />
8585
<Reference Include="System.Xml" />
8686
<Reference Include="System.Configuration" />
87+
<Reference Include="System.Web" />
88+
<Reference Include="System.Web.Extensions" />
89+
<Reference Include="System.Net.Http" />
8790
</ItemGroup>
8891
<PropertyGroup Condition=" '$(TargetFramework)' == 'net45'">
8992
<DefineConstants>NET45;NETFULL</DefineConstants>
@@ -113,4 +116,23 @@
113116
<None Include="..\..\README.md" Pack="true" PackagePath="\" />
114117
</ItemGroup>
115118

119+
<!-- Add configuration packages for NET8.0 target -->
120+
<ItemGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
121+
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
122+
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0" />
123+
</ItemGroup>
124+
125+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net452' ">
126+
<DefineConstants>NET452;NETFULL</DefineConstants>
127+
</PropertyGroup>
128+
<ItemGroup Condition=" '$(TargetFramework)' == 'net452' ">
129+
<Reference Include="System" />
130+
<Reference Include="System.Core" />
131+
<Reference Include="System.Configuration" />
132+
<Reference Include="System.Xml" />
133+
<Reference Include="System.Web" />
134+
<Reference Include="System.Web.Extensions" />
135+
<Reference Include="System.Net.Http" />
136+
</ItemGroup>
137+
116138
</Project>

Source/ChargifyDotNet/ChargifyDotNet.xml

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,8 +329,18 @@
329329
</member>
330330
<member name="M:ChargifyNET.ChargifyConnect.#ctor">
331331
<summary>
332-
Constructor
332+
Initializes a new instance of the ChargifyConnect class.
333+
</summary>
334+
</member>
335+
<member name="M:ChargifyNET.ChargifyConnect.#ctor(ChargifyNET.Configuration.IChargifyAccountConfig)">
336+
<summary>
337+
Initializes a new instance of the ChargifyConnect class using the specified account configuration.
333338
</summary>
339+
<remarks>This constructor simplifies initialization by allowing all required connection
340+
settings to be provided through a single configuration object. The values from the configuration are used to
341+
set up the connection to the Chargify service.</remarks>
342+
<param name="config">An object that provides the site, API key, API password, and shared key required to connect to the Chargify
343+
API. Cannot be null.</param>
334344
</member>
335345
<member name="M:ChargifyNET.ChargifyConnect.#ctor(System.String,System.String,System.String)">
336346
<summary>
@@ -2801,6 +2811,73 @@
28012811
The unit price for the component
28022812
</summary>
28032813
</member>
2814+
<member name="T:ChargifyNET.Configuration.ChargifyConfigFactory">
2815+
<summary>
2816+
Factory to obtain an IChargifyConfig across different target frameworks.
2817+
</summary>
2818+
</member>
2819+
<member name="T:ChargifyNET.Configuration.IChargifyConfig">
2820+
<summary>
2821+
Abstraction for retrieving Chargify account configuration across full .NET Framework and .NET (Core) targets.
2822+
</summary>
2823+
</member>
2824+
<member name="P:ChargifyNET.Configuration.IChargifyConfig.DefaultAccount">
2825+
<summary>
2826+
Returns configured default account name
2827+
</summary>
2828+
</member>
2829+
<member name="M:ChargifyNET.Configuration.IChargifyConfig.GetDefaultOrFirst">
2830+
<summary>
2831+
Retrieve the default (or first) account configuration.
2832+
</summary>
2833+
</member>
2834+
<member name="M:ChargifyNET.Configuration.IChargifyConfig.GetSharedKeyForDefaultOrFirstSite">
2835+
<summary>
2836+
Retrieve the shared key for the default (or first) account.
2837+
</summary>
2838+
</member>
2839+
<member name="P:ChargifyNET.Configuration.IChargifyConfig.UseJSON">
2840+
<summary>
2841+
Returns whether JSON should be used.
2842+
</summary>
2843+
</member>
2844+
<member name="T:ChargifyNET.Configuration.IChargifyAccountConfig">
2845+
<summary>
2846+
Cross-framework abstraction of a single account element.
2847+
</summary>
2848+
</member>
2849+
<member name="P:ChargifyNET.Configuration.IChargifyAccountConfig.Name">
2850+
<summary>
2851+
Gets the name associated with the current instance.
2852+
</summary>
2853+
</member>
2854+
<member name="P:ChargifyNET.Configuration.IChargifyAccountConfig.Site">
2855+
<summary>
2856+
Gets the site URL for the Chargify account.
2857+
</summary>
2858+
</member>
2859+
<member name="P:ChargifyNET.Configuration.IChargifyAccountConfig.ApiKey">
2860+
<summary>
2861+
Gets the API key used to authenticate requests to the service.
2862+
</summary>
2863+
</member>
2864+
<member name="P:ChargifyNET.Configuration.IChargifyAccountConfig.ApiPassword">
2865+
<summary>
2866+
Gets the API password used for authenticating requests to the external service.
2867+
</summary>
2868+
<remarks>The API password should be kept secure and not exposed in client applications or
2869+
logs. Access to this property may be restricted depending on the application's security model.</remarks>
2870+
</member>
2871+
<member name="P:ChargifyNET.Configuration.IChargifyAccountConfig.SharedKey">
2872+
<summary>
2873+
Gets the shared secret key used for authentication or encryption.
2874+
</summary>
2875+
</member>
2876+
<member name="P:ChargifyNET.Configuration.IChargifyAccountConfig.CvvRequired">
2877+
<summary>
2878+
Returns whether CVV is required.
2879+
</summary>
2880+
</member>
28042881
<member name="T:ChargifyNET.Coupon">
28052882
<summary>
28062883
Object representing coupon in Chargify

Source/ChargifyDotNet/ChargifyPage.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ protected ChargifyConnect Chargify
5656
var azureDeployed = UsefulExtensions.IsRunningAzure();
5757
if (!azureDeployed)
5858
{
59-
if (ConfigurationManager.GetSection("chargify") is ChargifyAccountRetrieverSection config)
59+
if (ConfigurationManager.GetSection("chargify") is ChargifyConfigSection config)
6060
{
6161
var accountInfo = config.GetDefaultOrFirst();
6262
_chargify.apiKey = accountInfo.ApiKey;

Source/ChargifyDotNet/Configuration/ChargifyAccountElement.cs renamed to Source/ChargifyDotNet/Configuration/ChargifyAccountConfig.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-

21
#region License, Terms and Conditions
32
//
4-
// ChargifyAccountElement.cs
3+
// ChargifyAccountConfig.cs
54
//
65
// Authors: Kori Francis <twitter.com/djbyter>, David Ball
76
// Copyright (C) 2010 Clinical Support Systems, Inc. All rights reserved.
@@ -38,7 +37,7 @@ namespace ChargifyNET.Configuration
3837
/// <summary>
3938
/// The object that holds information about a Chargify account, including name, site, apikey and password
4039
/// </summary>
41-
public class ChargifyAccountElement : ConfigurationElement
40+
public class ChargifyAccountConfig : ConfigurationElement, IChargifyAccountConfig
4241
{
4342
/// <summary>
4443
/// The "Name" of the site, used when setting the Default

Source/ChargifyDotNet/Configuration/ChargifyAccountElementCollection.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,15 @@ namespace ChargifyNET.Configuration
3838
/// <summary>
3939
/// The collection of Chargify Account elements in web.config
4040
/// </summary>
41-
[ConfigurationCollection(typeof(ChargifyAccountElement))]
41+
[ConfigurationCollection(typeof(ChargifyAccountConfig))]
4242
public class ChargifyAccountElementCollection : ConfigurationElementCollection
4343
{
4444
/// <summary>
45-
/// Create a new configuration element of type ChargifyAccountElement
45+
/// Create a new configuration element of type ChargifyAccountConfig
4646
/// </summary>
4747
protected override ConfigurationElement CreateNewElement()
4848
{
49-
return new ChargifyAccountElement();
49+
return new ChargifyAccountConfig();
5050
}
5151

5252
/// <summary>
@@ -56,19 +56,19 @@ protected override ConfigurationElement CreateNewElement()
5656
/// <returns></returns>
5757
protected override object GetElementKey(ConfigurationElement element)
5858
{
59-
return ((ChargifyAccountElement)element).Name;
59+
return ((ChargifyAccountConfig)element).Name;
6060
}
6161

6262
/// <summary>
6363
/// Get the account element by index
6464
/// </summary>
6565
/// <param name="index"></param>
6666
/// <returns></returns>
67-
public ChargifyAccountElement this[int index]
67+
public ChargifyAccountConfig this[int index]
6868
{
6969
get
7070
{
71-
return (ChargifyAccountElement)BaseGet(index);
71+
return (ChargifyAccountConfig)BaseGet(index);
7272
}
7373
set
7474
{
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#region License, Terms and Conditions
2+
3+
// ...existing license header...
4+
5+
#endregion
6+
7+
using System;
8+
using System.Collections.Generic;
9+
using System.Linq;
10+
#if NET8_0_OR_GREATER
11+
using Microsoft.Extensions.Configuration;
12+
#endif
13+
14+
namespace ChargifyNET.Configuration
15+
{
16+
#if NET8_0_OR_GREATER
17+
/// <summary>
18+
/// Default implementation of IChargifyConfig for modern .NET targets using
19+
/// Microsoft.Extensions.Configuration.
20+
/// Expected JSON structure:
21+
/// "Chargify": {
22+
/// "DefaultAccount": "main",
23+
/// "UseJSON": false,
24+
/// "Accounts": [ { "Name": "main", "Site": "https://....", "ApiKey": "...", "ApiPassword": "...", "SharedKey": "...",
25+
/// "CvvRequired": true } ]
26+
/// }
27+
/// </summary>
28+
public class ChargifyConfig : IChargifyConfig
29+
{
30+
private readonly IReadOnlyList<AccountConfig> _accounts;
31+
32+
public ChargifyConfig(IConfiguration configuration)
33+
{
34+
ArgumentNullException.ThrowIfNull(configuration);
35+
36+
var section = configuration.GetSection("Chargify");
37+
DefaultAccount = section["DefaultAccount"];
38+
UseJSON = bool.TryParse(section["UseJSON"], out var useJsonParsed) && useJsonParsed;
39+
40+
var accounts = new List<AccountConfig>();
41+
section.GetSection("Accounts").Bind(accounts);
42+
43+
_accounts = accounts;
44+
}
45+
46+
public string? DefaultAccount { get; }
47+
public bool UseJSON { get; }
48+
49+
public IChargifyAccountConfig GetDefaultOrFirst()
50+
{
51+
var acct = (!string.IsNullOrEmpty(DefaultAccount)
52+
? _accounts.FirstOrDefault(a => a.Name == DefaultAccount)
53+
: null) ??
54+
_accounts[0];
55+
56+
return acct ?? throw new InvalidOperationException("No Chargify accounts configured (Chargify:Accounts)");
57+
}
58+
59+
public string GetSharedKeyForDefaultOrFirstSite()
60+
{
61+
return GetDefaultOrFirst().SharedKey;
62+
}
63+
64+
private class AccountConfig : IChargifyAccountConfig
65+
{
66+
public string Name { get; } = string.Empty;
67+
public string Site { get; } = string.Empty;
68+
public string ApiKey { get; } = string.Empty;
69+
public string ApiPassword { get; } = string.Empty;
70+
public string SharedKey { get; } = string.Empty;
71+
public bool CvvRequired { get; set; }
72+
}
73+
}
74+
#endif
75+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#region License, Terms and Conditions
2+
// ...existing license header...
3+
#endregion
4+
5+
using System;
6+
#if NETFULL
7+
using System.Configuration;
8+
#endif
9+
#if NET8_0_OR_GREATER
10+
using Microsoft.Extensions.Configuration;
11+
#endif
12+
13+
namespace ChargifyNET.Configuration
14+
{
15+
/// <summary>
16+
/// Factory to obtain an IChargifyConfig across different target frameworks.
17+
/// </summary>
18+
public static class ChargifyConfigFactory
19+
{
20+
#if NETFULL
21+
public static IChargifyConfig Create()
22+
{
23+
if (ConfigurationManager.GetSection("chargify") is not ChargifyConfigSection section)
24+
{
25+
throw new ConfigurationErrorsException("Missing <chargify> section in configuration.");
26+
}
27+
28+
return section;
29+
}
30+
#endif
31+
#if NET8_0_OR_GREATER
32+
public static IChargifyConfig Create(IConfiguration configuration)
33+
{
34+
return new ChargifyConfig(configuration);
35+
}
36+
#endif
37+
}
38+
}

0 commit comments

Comments
 (0)