I've solved this in the past by just show the payment sources, and allowing the user to delete existing payment sources:
{% set paymentSources = craft.commerce.paymentSources.getAllPaymentSourcesByUserId(currentUser.id) %}
{% if paymentSources %}
<table>
<thead>
<tr>
<th scope="col">
Card details
</th>
<th scope="col">
Actions
</th>
</tr>
</thead>
<tbody>
{% for paymentSource in paymentSources %}
<tr>
<td>{{ paymentSource.description }}</td>
<td>
<form method="post">
{{ csrfInput() }}
{{ actionInput('commerce/payment-sources/delete') }}
{{ redirectInput(entry.uri) }}
<input type="hidden" name="id" value="{{ paymentSource.id }}"/>
<button type="submit">Delete</button>
</form>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endif %}
You can then show a form to add a new payment source:
{% set customer = craft.commerce.customers.getCustomer() %}
{#
Note: this next line fetches a single subscription - if you support multiple
subscriptions, you'll need to do it a little differently
#}
{% set currentSubscription = customer.getSubscriptions()|first %}
{% set plan = craft.commerce.plans.getPlanById(currentSubscription.planId) %}
{% set gateway = plan.getGateway() %}
<form method="post" accept-charset="UTF-8" autocomplete="off" novalidate>
{{ csrfInput() }}
{{ actionInput('commerce/payment-sources/add') }}
{{ redirectInput(entry.uri) }}
<input type="hidden" name="gatewayId" value="{{ gateway.id }}">
{{ gateway.getPaymentFormHtml({})|raw }}
<button type="submit">Add card</button>
</form>
This is probably the simplest answer to your question, though you can get creative with how you present the payment sources and the add card form to the user.